signin

准备

signin.exe:https://wwa.lanzous.com/inIQdec11zi

程序分析

可以判断出,这个程序实际上是由Python打包成的可执行文件,且在运行这个程序时,在同目录下产生了一个tmp.dll文件,猜测是程序调用某些函数的接口。

反编译

使用archive_viewer.py反编译为字节码文件

python archive_viewer.py signin.exe

修补文件

55 0D 0D 0A 00 00 00 00 70 79 69 30 10 01 00 00

程序是在Python3.8环境下打包,因此我们需要在Python3.8下使用uncompyle6

uncompyle6 main.pyc > main.py

得到py文件

 1 # uncompyle6 version 3.7.22 # Python bytecode 3.8 (3413)3 # Decompiled from: Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:37:50) [MSC v.1916 64 bit (AMD64)]4 # Embedded file name: main.py5 # Compiled at: 1995-09-28 00:18:566 # Size of source mod 2**32: 272 bytes7 import sys8 from PyQt5.QtCore import *9 from PyQt5.QtWidgets import *
10 from signin import *
11 from mydata import strBase64
12 from ctypes import *
13 import _ctypes
14 from base64 import b64decode
15 import os
16
17 class AccountChecker:
18
19     def __init__(self):
20         self.dllname = './tmp.dll'
21         self.dll = self._AccountChecker__release_dll()
22         self.enc = self.dll.enc
23         self.enc.argtypes = (c_char_p, c_char_p, c_char_p, c_int)
24         self.enc.restype = c_int
25         self.accounts = {b'SCTFer': b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')}
26         self.try_times = 0
27
28     def __release_dll(self):
29         with open(self.dllname, 'wb') as (f):
30             f.write(b64decode(strBase64.encode('ascii')))
31         return WinDLL(self.dllname)
32
33     def clean(self):
34         _ctypes.FreeLibrary(self.dll._handle)
35         if os.path.exists(self.dllname):
36             os.remove(self.dllname)
37
38     def _error(self, error_code):
39         errormsg = {0:'Unknown Error',
40          1:'Memory Error'}
41         QMessageBox.information(None, 'Error', errormsg[error_code], QMessageBox.Abort, QMessageBox.Abort)
42         sys.exit(1)
43
44     def __safe(self, username: bytes, password: bytes):
45         pwd_safe = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
46         status = self.enc(username, password, pwd_safe, len(pwd_safe))
47         return (pwd_safe, status)
48
49     def check(self, username, password):
50         self.try_times += 1
51         if username not in self.accounts:
52             return False
53         encrypted_pwd, status = self._AccountChecker__safe(username, password)
54         if status == 1:
55             self._AccountChecker__error(1)
56         if encrypted_pwd != self.accounts[username]:
57             return False
58         self.try_times -= 1
59         return True
60
61
62 class SignInWnd(QMainWindow, Ui_QWidget):
63
64     def __init__(self, checker, parent=None):
65         super().__init__(parent)
66         self.checker = checker
67         self.setupUi(self)
68         self.PB_signin.clicked.connect(self.on_confirm_button_clicked)
69
70     @pyqtSlot()
71     def on_confirm_button_clicked(self):
72         username = bytes((self.LE_usrname.text()), encoding='ascii')
73         password = bytes((self.LE_pwd.text()), encoding='ascii')
74         if username == b'' or password == b'':
75             self.check_input_msgbox()
76         else:
77             self.msgbox(self.checker.check(username, password))
78
79     def check_input_msgbox(self):
80         QMessageBox.information(None, 'Error', 'Check Your Input!', QMessageBox.Ok, QMessageBox.Ok)
81
82     def msgbox(self, status):
83         msg_ex = {0:'',
84          1:'',
85          2:"It's no big deal, try again!",
86          3:'Useful information is in the binary, guess what?'}
87         msg = 'Succeeded! Flag is your password' if status else 'Failed to sign in\n' + msg_ex[(self.checker.try_times % 4)]
88         QMessageBox.information(None, 'SCTF2020', msg, QMessageBox.Ok, QMessageBox.Ok)
89
90
91 if __name__ == '__main__':
92     app = QApplication(sys.argv)
93     checker = AccountChecker()
94     sign_in_wnd = SignInWnd(checker)
95     sign_in_wnd.show()
96     app.exec()
97     checker.clean()
98     sys.exit()
99 # okay decompiling main.pyc

代码分析

通过代码我们能够了解到这些信息

1.

elf.dllname = './tmp.dll'

调用了tmp.dll文件作为接口。

2.

    def __safe(self, username: bytes, password: bytes):pwd_safe = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'status = self.enc(username, password, pwd_safe, len(pwd_safe))return (pwd_safe, status)def check(self, username, password):self.try_times += 1if username not in self.accounts:return Falseencrypted_pwd, status = self._AccountChecker__safe(username, password)if status == 1:self._AccountChecker__error(1)if encrypted_pwd != self.accounts[username]:return Falseself.try_times -= 1return True

self.accounts = {b'SCTFer': b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')}

调用tmp.dll文件中的enc函数,传入username, password, pwd_safe, len(pwd_safe),实际就是将password加密后存储到pwd_safe字节码中。最后用pwd_safe与b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')比较,且我们能够了解到用户名应该是SCTFer,且最后返回的status一个为非1。

打开tmp.dll文件,找到enc函数

观察代码,实际操作可以分为两部分,逆向分析

异或操作

第47~54行代码实际上就是将Dst与用户名循环异或,最后得到b64decode(b'PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA'),因此我们只需要逆向异或就能得到加密后的Dst

from base64 import *username = "SCTFer"
pwd_safe = b64decode('PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')
# print (len(pwd_safe))
num = ["%02x" % x for x in pwd_safe]
hex_num = [int(x, 16) for x in num]print(num)
# print (len(num))
for i in range(32):hex_num[i] ^= ord(username[i % len(username)])
# print (hex_num)
hex_nums = bytes.fromhex(''.join([hex(x)[2:].rjust(2, '0') for x in hex_num]))print (hex_nums)

得到

b'o\xf2\x96\xfd\x82\x9c\xde\xb52v\x86yK3\xe6\x1f\x06\xd8\xb7=\x13J\xb8\xe3\xb52\xb3\xd38\x86\x10\x02\x00'

加密操作

每次传入了8字节数据进行加密(总共64字节),打开sub_180011311函数

仔细观察代码,实际上这部分代码是使用CRC32的查表法,对数据进行加密。

加密原理实际上就是CRC32算法---输入一组长度32的字符串,每8个字节分为1组,共4组。对每一组取首位,判断正负。正值,左移一位;负值,左移一位,再异或0xB0004B7679FA26B3。重复判断操作64次,得到查表法所用的表。

因此我们只需要将整个加密过程逆向操作得到查表法的表,再进行CRC32计算,就能得到输入。

secret = []# for i in range(4):
#     secret.append(int(hex_nums[i*8:(i + 1) * 8][::-1].hex(),16))for i in range(4):secret.append(int.from_bytes(hex_nums[i*8:(i + 1) * 8], byteorder="little"))print (secret)key = 0xB0004B7679FA26B3flag = ""for s in secret:for i in range(64):sign = s & 1if sign == 1:s ^= keys //= 2if sign == 1:s |= 0x8000000000000000print(hex(s))j = 0while j < 8:flag += chr(s&0xFF)s >>= 8j += 1
print(flag)

因为计算机中采用小端排序,因此需要注意分组倒序。得到

脚本

from base64 import *username = "SCTFer"
pwd_safe = b64decode('PLHCu+fujfZmMOMLGHCyWWOq5H5HDN2R5nHnlV30Q0EA')
# print (len(pwd_safe))
num = ["%02x" % x for x in pwd_safe]
hex_num = [int(x, 16) for x in num]print(num)
# print (len(num))
for i in range(32):hex_num[i] ^= ord(username[i % len(username)])
# print (hex_num)
hex_nums = bytes.fromhex(''.join([hex(x)[2:].rjust(2, '0') for x in hex_num]))print (hex_nums)secret = []# for i in range(4):
#     secret.append(int(hex_nums[i*8:(i + 1) * 8][::-1].hex(),16))for i in range(4):secret.append(int.from_bytes(hex_nums[i*8:(i + 1) * 8], byteorder="little"))print (secret)key = 0xB0004B7679FA26B3flag = ""for s in secret:for i in range(64):sign = s & 1if sign == 1:s ^= keys //= 2if sign == 1:s |= 0x8000000000000000print(hex(s))j = 0while j < 8:flag += chr(s&0xFF)s >>= 8j += 1
print(flag)

get flag!

username:SCTFer

password:SCTF{We1c0m3_To_Sctf_2020_re_!!}

get_up

这是实际就是在说明输入字符串长度不大于6,且通过sub_401DF0返回1

返回1,实际就是满足加密后的Dst与32c1d123c193aecc4280a5d7925a2504相同,实际是MD5加密,得到输入为sycsyc

接下来,程序查找.reioc段,并将.reioc段数据与sycsyc循环异或,写出IDAPython脚本

beg_adr = 0x405000
dst = "sycsyc"
for i in range(0,0x200,16):for j in range(16):PatchByte(beg_adr+i+j,Byte(beg_adr+i+j)^ord(dst[j%6]))

然后跟去混淆方法类似,先转换为Data,再强制分析数据,转换为函数。

打开sub_4027F0函数

这里实际和上面差不多,输入长度为30的flag,取前5个字符,与.ebata段的部分循环异或。(实际就是得到下面sub_404000函数的代码)。这里可以合理猜测flag的前五个字符为SCTF{,写出脚本

beg_adr = 0x404000
dst = "SCTF{"
for i in range(16,96):PatchByte(beg_adr+i,Byte(beg_adr+i)^ord(dst[i%5]))

跟上面一样将.ebata段转换为data,再分析代码,转换为函数,开头如果提示栈不平衡,重新单独分析一下就行。

这里一个函数实际就是RC4生成S-BOX,另一个函数就是加密函数,key值为syclover

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef unsigned longULONG;#pragma warning(disable:4996)/*初始化函数*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len)
{int i = 0, j = 0;char k[256] = { 0 };unsigned char tmp = 0;for (i = 0; i < 256; i++){s[i] = i;k[i] = key[i % Len];}for (i = 0; i < 256; i++){j = (j + s[i] + k[i]) % 256;tmp = s[i];s[i] = s[j]; // 交换s[i]和s[j]s[j] = tmp;}
}/*加解密*/
void rc4_crypt(unsigned char* s, unsigned char* Data, unsigned long Len)
{int i = 0, j = 0, t = 0;unsigned long k = 0;unsigned char tmp;for (k = 0; k < Len; k++){i = (i + 1) % 256;j = (j + s[i]) % 256;tmp = s[i];s[i] = s[j]; // 交换s[x]和s[y]s[j] = tmp;t = (s[i] + s[j]) % 256;Data[k] ^= s[t];}
}int main()
{unsigned char s[256] = { 0 }, s2[256] = { 0 }; // S-boxchar key[256] = { "syclover" };char pData[512] = { 0 };int v12[] = { 128,85,126,45,209,9,37,171,60,86,149,196,54,19,237,114,36,147,178,200,69,236,22,107,103,29,249,163,150,217 };int i;//char m[32] = { 0 };for (int i = 0; i < 30; ++i) {pData[i] = (char)v12[i];}unsigned long len = strlen(pData);printf("pData=%s\n", pData);printf("key=%s,length=%d\n\n", key, strlen(key));rc4_init(s, (unsigned char*)key, strlen(key)); // 已经完成了初始化printf("完成对S[i]的初始化,如下:\n\n");for (i = 0; i < 256; i++){printf("%02X", s[i]);if (i && (i + 1) % 16 == 0)putchar('\n');}printf("\n\n");for (i = 0; i < 256; i++) // 用s2[i]暂时保留经过初始化的s[i],很重要的!!!{s2[i] = s[i];}printf("已经初始化,现在解密:\n\n");printf("len = %d\n", len);rc4_crypt(s, (unsigned char*)pData, len); printf("pData=%s\n\n", pData);system("PAUSE");return 0;
}

SCTF{zzz~(|3[___]_rc4_5o_e4sy}

Can you hear

音频文件:https://wwa.lanzous.com/iMv4zecuw3g

音频是SSTV,用软件Robot36接受音频中的数据

doudizhu

只要将手里牌出完,就能获得flag

2020 SCTF 部分WriteUp相关推荐

  1. 从SCTF看JWT安全 (附SCTF web writeup)

    原创作者:Fz41 这两天在打SCTF,有一题涉及到JWT的简单的知识,现在来把JWT相关的知识汇总一下,虽然不是主要的考察内容,但是作为一个基础知识,还是要掌握的. JWT技术介绍 来源 用户认证的 ...

  2. 网鼎杯2020 朱雀部分writeup

    Reverse –go 替换了base64字典 解题思路: 程序将我们输入的key,经过换了编码字典的base64加密后,去掉最后==,计算key加密后的长度,长度为0x16,就开始解密.那么我们只需 ...

  3. 安恒月赛2020元旦场Writeup

    正在复习密码学,突然发现今天有月赛了-于是做了一下,很简单的一次题目.. Web1 进入后是一个JS的游戏,在index.js里修改一下destroyed的增加,设置大一些,如下: 然后随便玩一下就能 ...

  4. [hackinglab][CTF][综合关][2020] hackinglab 综合关 writeup

    [hackinglab][CTF][解密关][2020] hackinglab 上传关 writeup 综合关 1 渗透测试第一期 概要:在于绑定admin用户,进行修改密码!(手机号码仔细!!!) ...

  5. i春秋2020新春公益赛WEB复现Writeup

    i春秋2020新春公益赛WEB复现Writeup 说实话这个比赛打的我是一点毛病都没有,还是觉得自己掌握的东西太少了,,, 尤其是sql注入,都被大佬们玩出花来了,可能自己太菜,,,哭了!!! 关于S ...

  6. 2020羊城杯CTF随缘Writeup

    2020羊城杯CTF随缘Writeup docker源码链接: https://github.com/k3vin-3/YCBCTF2020 Web部分 a_piece_of_java 考点:源码审计. ...

  7. 2020年第二届“网鼎杯”网络安全大赛 白虎组 部分题目Writeup

    2020年第二届"网鼎杯"网络安全大赛 白虎组 部分题目Writeup 2020年网鼎杯白虎组赛题.zip下载 https://download.csdn.net/download ...

  8. WUST-CTF 2020 WriteUp

    WUST-CTF 2020 WriteUp 前言 Web checkin admin CV Maker 朴实无华 Crypto 大数运算 情书 B@se babyrsa 佛说:只能四天 Misc Sp ...

  9. 2020网鼎杯青龙组部分题目writeup

    2020网鼎杯青龙组部分题目writeup 0x00 Crypto之boom 0x00 Crypto之boom 下载下来是个exe文件,拖到cmd运行(切记一定不要双击,用cmd打开,双击运行后最后程 ...

最新文章

  1. pytorch笔记:实现简易LSTM
  2. 2020-10-11 LMI线性矩阵不等式的一些知识
  3. Hadoop大数据——mapreduce的Distributed cache
  4. C语言写的程序如何控制计算机硬件
  5. 年轻代为什么要设置两个Survivor区
  6. android重置系统,安卓手机越用越卡,恢复出厂设置真有用?别瞎搞,看完就明白了!...
  7. Word2vec加TextRank算法生成文章摘要
  8. numpy 最大值_第 85 天:NumPy 统计函数
  9. .net System.Web.Mail发送邮件
  10. Bringing up interface eth0: Error: Connection activation failed: Device not managed by NetworkMan
  11. 计算机vb题库程序代码编写,11计算机专业VB试题(二)
  12. Python操作Excel制作可视化数据图,实现自动化办公
  13. 如何加载CASS DAT格式文件
  14. NLP+词法系列(一)︱中文分词技术小结、几大分词引擎的介绍与比较
  15. 【题目回顾】广工大2020年10月ACM第一次月赛B题--Dio的面包工坊
  16. C语言斐波那切数列数列求和
  17. Ubuntu / Windows 查看域名系统 (Domain Name System,DNS)
  18. c语言去掉文件中重复单词,awk脚本 使用awk去掉重复的单词
  19. oraclenbsp;level关键字
  20. 三菱fx2n64mr说明书_FX2N-64MR-DS手册三菱可编程控制器FX2N-64MR-DS使用说明书 - 广州凌控...

热门文章

  1. 北京对无人车的热情,华尔街都感受到了-1
  2. js网状特效源代码下载
  3. 安卓轻量级线上APM监测
  4. 微信小程序分页(超简单)
  5. 出口退税率6月20日准时出通知了!客户下单了!
  6. 医疗手术机器人 双目视觉导航方式产品汇总
  7. 《C语言实战教学》:程序式思维及C语言介绍
  8. 使用Excel制作一个动态计划表
  9. 何朝曦:构建云化安全能力的三个建议
  10. Python之学生信息管理系统