日常吐槽

六级怎么这么难啊,抛开没复习不谈,不应该让我至少看得懂文章吗???

这次完成的是文件上传和了解了一些反序列化漏洞,我看了一下java反序列化漏洞,我的老天爷呀啊,那payload怎么能这么长,我感觉学这个漏洞需要把java语言的知识给补一下,😔

文件上传漏洞

文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果.

绕过

前端验证

javascript代码验证 ( labs-1),打开F12删除相关js代码即可

MIME验证 (labs-2),通过抓包修改Content-Type值绕过检测

黑名单

后缀名限制不足 (lab-3),可以用.phtml,.php3等不是php后缀但是服务器可以解析php代码的后缀名实现绕过

上传.htaccess配置文件 (lab-4),上传.htaceess配置文件可以让服务器根据自己写的内容去包含不同后缀的文件,那么我们可以用如下内容,意思是用php解析文件名中包含xxx的文件,以此完成绕过

Plain Text
<FilesMatch "XXX">
 SetHandler application/x-httpd-php
</FilesMatch>

上传.user.ini配置文件 (lab-5),通过添加auto_prepend_file的内容可以让目录中的php文件自动包含指定的文件,那么我们只需要上传一个图片马和.user.ini文件就可以实现绕过,前提是目录中存在.php后缀的文件

点绕过和空格绕过 ,原理是利用windows处理文件名后缀的特性,它会自动删除文件名后的.和空格,但是如果后端代码没有对文件名后面的内容进行处理的话,点和空格刚好可以绕过黑名单的匹配从而实现绕过

双写绕过 ,如果发现后端代码处理黑名单中的违规后缀是替换为空且只进行一次,那么我们就可以用.pphphp来实现当执行替换为空的操作后依然存在.php后缀从而绕过

大小写绕过 ,如果没有相应的代码检测的话,由于操作系统的特性,对大小写不敏感,所以文件名后缀大小写都能识别

 ::$DATA绕过 后端Windows的环境下,在php文件后缀名后面加上“::DATA”后面的数据当作文件流处理,而不会检查后缀名,上传成功后,系统会自动将“ ::$DATA “去除

白名单与文件包含

%00截断,前提是上传路径可以修改(如GET,POST),且php版本过低(基本用不了这个方法)。当我们可以控制路径且发现文件名存在拼接,那么我们可以在路径后面加上xxx.php%00 截断后续拼接的同时还可以把文件内容上传至php后缀的文件中,如果是POST传参,那么%00需要解码后在传参

白名单很少有直接绕过的方法,一般需要搭配文件包含漏洞一起食用

一般的文件上传代码都会检测上传的到底是不是图片,所以黑名单验证和很少会存在,但当我们发现该地方还存在文件包含漏洞,就可以实现绕过

步骤一般是先上传图片马,然后在到有文件包含的地方去包含该文件即可,但要注意代码一定不要放到开头,也不能只有代码,没有相应的文件头

二次渲染 ,如果后端对上传文件进行了二次渲染操作,那么可能我们放在图片里的代码就会被优化,这里通常使用GIF动图去上传,因为GIF被二次渲染后仍然会有一部分内容不会改变,那么我们就可以把相应的代码放在那部分中,如果要用.jpg和.png则要困难很多,要用大佬开发的代码去找哪里没有被改变

条件竞争 如果发现代码中存在先上传文件到服务器,在对该文件进行匹配验证,那么就可以用条件竞争。

步骤一般为先上传php文件抓包,用burp重复发包,然后在浏览器(不是抓包的那个)中快速重复访问该php文件的地址,就有概率让php代码运行,只要运行成功,那么服务器就无法删除该文件,那么就可以看到想要的结果

逻辑数组拼接 ,根据源代码对filename的操作传入相应的数组从而实现绕过,具体看lab-21

突破文件路径绕过

 在文件上传时,程序通常允许用户将文件放到指定的目录中,如果指定的目录存在,就将文件写入目录中,不存在的话则先建立目录,然后写入。比如:在前端的HTML代码中,有一个隐藏标签<input type="hidden" name="Extension" value="up"/> 在服务器端有如下代码 if(!is_dir($Extension);

    }

攻击者可以利用工具将表单中value的值由“up”改为“pentest.asp”,并上传一句话图片木马文件。程序在接收到文件后,对目录判断,如果服务器不存在pentest.asp目录,将会建立此目录,然后再将图片一句话密码文件写入pentest.asp目录,如果Web容器为IIS  6.0,那么网页木马会被解析

序列化和反序列化

序列化和反序列化基础

  • 序列化 是将 PHP 对象转换为字符串的过程,可以使用 serialize() 函数来实现。该函数将对象的状态以及它的类名和属性值编码为一个字符串。序列化后的字符串可以存储在文件中,存储在数据库中,或者通过网络传输到其他地方。
  • 反序列化 是将序列化后的字符串转换回 PHP 对象的过程,可以使用 unserialize() 函数来实现。该函数会将序列化的字符串解码,并将其转换回原始的 PHP 对象

这是序列化前的样子

这是序列化后的样子

序列化格式中的字母含义

a - array                          b - boolean 

d - double                        i - integer

o - common object            r - reference

s - string                          C - custom object

O - class                           N - null

R - pointer reference          U - unicode string

值得注意的是:

观察不同类型变量名的字符长度标识,你会发现长度和你看到的好像有些不一样,那是因为在 protected private 类型的变量中都加入了不可见字符:

如果是 protected 变量,则会在变量名前加上\x00*\x00

如果是 private 变量,则会在变量名前加上\x00类名

魔术方法  

点击图片可查看完整电子表格

POP链构造

逻辑性如链条一般,通过一个攻击点去找到另一条攻击点,层层溯源最终拿到漏洞,魔术方法可以作为其中的节点

把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP  CHAIN 。此时类中所有的敏感属性都属于可控的。当unserialize()传入的参数可控,便可以通过反序列化漏洞控制POP  CHAIN达到利用特定漏洞的效果。

POP链小技巧

  • POP链常出现的函数
  • 命令执行:exec()passthru()popen()system()
  • 文件操作:file_put_contents()file_get_contents()unlink()
  • 代码执行:eval()assert()call_user_func()
  • 大写S可以支持字符串编码

s:4:"user"; -> S:4:"use\72";

  • 深浅copy

在php中如果我们使用 & 对变量A的值指向变量B,这个时候是属于浅拷贝,当变量B改变时,变量A也会跟着改变。在被反序列化的对象的某些变量被过滤了,但是其他变量可控的情况下,就可以利用浅拷贝来绕过过滤

$A=&$B;

  • 利用php伪协议

session反序列化漏洞

PHP在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取 $_SESSION 数据,都会对数据进行序列化和反序列化,PHP中的Session的实现是没有的问题的,漏洞主要是由于使用不同的引擎来处理session文件造成的。

pchar反序列化漏洞利用

字符串逃逸

Writeup

Upload-labs

1

  • 根据提示,该代码是前端js验证

  • F12打开开发者模式,找到checkfile函数然后删除即可上传php文件

  • 传入成功后右击页面中的图片复制图片链接然后用蚁剑链接即可拿到flag

2

  • 根据提示和源码可以得出这关是MIME验证,但是MIME可以在客户端抓包直接修改

  • 先选择包含一句话代码的php文件上传然后抓包修改Content-Type 参数为image/jpeg绕过检测,在右边可以直接看到上传后的文件地点

  • 用蚁剑连接即可

3

  • 源代码显示这关是黑名单验证,但是只过滤了一个php后缀,服务器对php文件解析的后缀存在多种,可以换成别的可以解析php代码的后缀

  • 上传成功后依旧复制图片地址然后用蚁剑连接就可以了

注意如果用的是旧版本小皮面板需要修改httped-config文件,在文件中添加AddType application/x-httpd-php .php .phtml .php5 .php3服务器才能识别不同后缀的php代码并解析执行

4

  • 提示显示基本把所有能后缀绕过的都过滤掉了,但是没有过滤.htaccess配置文件

  • 那么接下来我们可以先构造.htaccess文件让服务器把我们指定的文件用php代码来解析,然后分别传入.htaccess文件和带有一句话木马的图片码即可

  • 上传成功后用蚁剑连接即可

注意这里的.htaccess绕过需要php是老版本不是nts版本,所以不能用新版小皮搭建这个靶场,需要用2018版本或其他旧版本

5

这关的过滤加上了.htaccess,那么可以尝试从源代码的只过滤一次的性质入手

  • 源码的主要的过程是先清空格和最后的点号,然后找到最后一个后缀前的点号,然后把点号前面的内容删除只留下一个后缀名与黑名单作比较,但因为只删除一次,不是循环,那么我们可以在修改文件名后缀为.php. .(注意中间的空格)绕过

  • 这关也可以上传.user.ini文件在里面配置auto_prepend_file = shell.jpg,让目录下的所有php文件包含shell.jpg里的内容,利用自带的readme.php去包含上传的图片马

6

  • 这题把小写函数删除了,那么我们直接大写后缀名.Php就可以绕过了

7

这关的绕过和第5关一样,只是不能用.user.ini配置文件了

8

因为这关删除了去末尾点的函数,所以也是和第5关类似.php.(可以没有空格)

9

  • 这关把对::$DATA的过滤删除了,那么我们就可以在文件后缀名后加上它绕过后缀名检查

10

不知道为什么这关源代码似乎与第五关一样,没看出有什么区别,虽然提示是说这是白名单验证,但是依旧可以用第五关的方式过

11

  • 这道题换成了把黑名单内的后缀改为空,但是只进行了一次,所以可以用双写绕过

12

  • 从源码可以看出这题上传文件的路径是由GET参数save_path拼接的,那么我们可以通过控制该参数去修改上传后的文件地址

  • 先上传一个图片马然后抓包,修改GET参数为1.php%00,这样就可以让图片马的内容上传到该php后缀的文件中让php代码可以运行

注意:%00截断需要php版本低于5.3且不能开启魔术引号

13

  • 这道题把参数改为了POST,只需要在POST里面修改上传路径就可以了,但是%00需要urldecode

14

  • 根据题目提示先创造一个图片马,然后上传上去

  • 因为有文件头验证,那么我们把代码放到图片的最后面

  • 找到带有文件包含的地方输入图片马的上传地址,看到phpinfo()运行成功,之后把phpinfo改为一句话木马即可

注意:这里要把php版本改回5.4以上才行

15

  • 这道题对上传文件是否为图片进行了更严格的检测

  • 其他步骤和上一题一样,只需要把一句话木马放在图片最后即可

16

  • 根据提示开启相关功能然后上传图片马

  • 其他步骤和前几题一样

17

  • 这关是二次渲染的绕过,防止恶意代码被优化掉

  • 上传图片,下载回显的图片,分析回显的图片和原图片的HEX相同之处,并在原图片相同点处插入恶意 脚本 (一句话木马),而图片一般使用GIF,因为比较简单

  • 可以看到上传后的图片经过二次渲染依旧藏有一句话木马,然后用蚁剑连接即可

18

  • 这题的源码显示上传流程是下先把文件通过设置路径上传到指定目录,然后在白名单检测,如果不合格就删除

  • 可以利用条件竞争来上传文件,只要利用burp多线程重复发包,总会有一瞬间这个.php文件会上传上目标服务器,然后在浏览器中反复访问就可以看到结果

19

  • 提示代码审计,看完发现就跟检测上传是否为图片是一样的功能,还有重命名功能,只需要上传图片马然后找到上传地址,在利用文件包含就可以执行相关代码

20

  • 这关可以让我们修改上传后的文件名称,然后是黑名单限制

  • 那么就可以用前面关卡中的大小写、加点,加%00截断等方式去绕过

21

  • 这关是MIME验证和白名单验证同时进行

  • 通过抓包,我们也可以同时修改Content-Type参数和上传文件的名称,源代码中对$file名称进行了数组化并在最后进行了数组拼接,根据拼接逻辑可以初始化file数组arr[0]=1.php,arr[2]=jpg,那么最后拼接出来的就是1.php.了

PHPserialize-labs

1

  • 这关提示我们把类FLAG实例化就可以得到答案

2

  • 这一关的关键是要把flag_string的值赋给free_flag

  • 那我们只需要利用eval函数执行相应代码即可

3

  • 这关是考察对象中值的权限,私有变量和保护变量是不能在外部直接被引用的,所以我们需要借助类里面的get函数去输出相应的值

4

  • 这关变量全部为私有变量,无法直接输出,那么可以利用序列化函数直接把整个对象全部序列化后然后输出

  • 然后拼接出里面flag的内容为:FLAG{ser4l1ze2se3me}

5

  • 根据题目要求构造相应的序列化字符串即可

6

  • 这关也是给相应的变量赋值,不过要注意不同类型的变量protected和private在序列化后会出现%00空字符

7

这题查攻略发现无法用windows搭建的环境去解题,所以我换用了linux重新搭建了一遍环境

  • 原理很简单,只需要把变量的值改为我们想要实现的功能就行

8

  • 这关是让我们反复析构函数使得__destruct的调用次数超过4次便会输出flag,有提示可以知道serialize + unserialize会重新创建一个对象,那只要我们反复4次便可以创造出4个对象,结束后就成功多次触发__destruct了

9

  • 这题环境和第7关一样,解题步骤也一样,但似乎没有放flag

10

  • 这关是利用__wakeup()方法在反序列化时自动调用的特性

11

  • 根据提示修改php版本,然后更改对象属性的值即可

  • 但是发现flag是错的

  • 查看源代码发现需要在根目录下有flag文件才行,但是我下载的源代码里没有flag文件,所以输出的错误

12

  • 这题是让我们拼接出完整的句子通过固定的chance和随机两个变量

  • 我们只需要一个一个访问然后记录下每个变量分别是什么最后在拼接在一起即可,但是class FLAG和class  CHALLNGE中的$f和$l是不同的,需要通过在变量前加上%00FLAG%00才能访问到FLAG中的变量,最终得到flag

13

  • 根据_tostring特性,当我们把对象当成字符串打印出来的时候会自动反应

14

  • 这题是利用__invoke特性

  • 一直看错误的flag不得劲,所以用docker创建了一个flag

15

  • 根据题目结构可知要先实例化D对象从而在反序列化后激活__wakeUp,调用顺序是D->destnation->A->B->C

  • 根据调用顺序构造相应的序列化对象

Java
<?php
class A {
    public $a;
    public function __construct($a) {
        $this->a = $a;
    }
}
class B {
    public $b;
    public function __construct($b) {
        $this->b = $b;
    }
}
class C {
    public $c;
    public function __construct($c) {
        $this->c = $c;
    }
}

class D {
    public $d;
    public function __construct($d) {
        $this->d = $d;
    }
    public function __wakeUp() {
        $this->d->action();
    }
}

class destnation {
    var $cmd;
    public function __construct($cmd) {
        $this->cmd = $cmd;
    }
    public function action(){
        eval($this->cmd->a->b->c);
    }
}

echo "o=".serialize(new D(new destnation(new A(new B(new C("system('cat flag.php');"))))));

  • 查看源代码发现需要cat的文件是/flag,那就把new C()里面的flag.php改成/flag

16

  • 这题需要用__wakeUp里面的echo函数去激活__toString,再利用__toString中的return $f()去激活__invoke

  • 根据调用顺序构造相应的序列化对象

Bash
<?php
class A {
    public $a;
    public function __construct($a) {
        $this->a = $a;
    }
    public function __invoke() {
            include $this->a;
            return $flag;
    }
}

class B {
    public $b;
    public function __construct($b) {
        $this->b = $b;
    }
    public function __toString() {
        $f = $this->b;
        return $f();
    }
}

class INIT {
    public $name;
    public function __construct($name) {
        $this->name = $name;
    }
    public function __wakeUp() {
        echo $this->name.' is awake!';
    }
}
echo "o=".serialize(new INIT(new B(new A('/flag'))));

17

  • 题目说只要实际数量匹配,其他变量修改不会检查的话,那我们就构造一个B对象符合题目要求,然后在把B替换成A就可以了

  • 用代码输出更方便

Java
<?php
class A {

}
class B {
    public $helloctfcmd = "get_flag";
    protected $b = "CTF";
    private $c = "FLAG{TEST}";
}
$b= serialize(new B());
echo urlencode(str_replace('B', 'A', $b));

18

  • 这关只需要把对应的字符串长度和类名修改一下就可以拿到flag

Bash
<?php
class Demo {
    public $a = "Hello";
    public $b = "CTF";
    public $key = 'GET_FLAG";}FAKE_FLAG';
}

$a=serialize(new Demo());
$b='O:4:"FLAG":3:{s:1:"a";s:5:"Hello";s:1:"b";s:3:"CTF";s:3:"key";s:8:"GET_FLAG";}FAKE_FLAG";}';
echo "?target=".$a."&"."change=".$b;

[SQCTF]

  • 这题的过滤何意味啊

  • 直接构造题目要求的序列化对象即可

Bash
<?php
class test{
    var $user = 'test';
    var $pswd = 'escaping';
    function __construct($user){
        $this->user=$user;
    }
}
echo serialize(new test('test'));

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐