下载附件,填上题目地址,用户名密码都为admin
花钱买flag,Proxifier配合burp抓包
正常的回显为
通过抓包发现,请求除了包含基本的请求体,还有一个token。每次token请求均不同,且更改请求体后token失效,请求也无法重放,所以推断出token必然是以一定规则在本地生成的
把安装文件解压
把app.asar文件用工具解包(懒得安装环境 网上下了个工具)
http://www.pc6.com/softview/SoftView_799231.html
首先查看package.json
页面是基于vue构建的,同时也发现了库crypto和js-md5。所以即使js源文件被webpack,也可以通过搜索methods关键词找到token生成函数的位置,同时推测token加密使用了md5和另一种加密方式
再查看/dist/electron/renderer.js
用sublime的插件格式化整个文档
整理后得到的代码为
methods: {
encrypt: function(e, i, t) {
var o = this;
return c()(a.a.mark((function n() {
return a.a.wrap((function(o) {
for (;;) switch (o.prev = o.next) {
case 0:
return o.abrupt("return", new s.a((function(o) {
var n = p.a.createCipheriv("aes-128-cbc", e, i),
r = n.update(t, "utf8", "binary");
r += n.final("binary"),
o(r = new Buffer.from(r, "binary").toString("hex"))
})));
case 1:
case "end":
return o.stop()
}
}), n, o)
})))()
},
makeToken: function(e) {
var i = this;
return c()(a.a.mark((function t() {
var o, r;
return a.a.wrap((function(t) {
for (;;) switch (t.prev = t.next) {
case 0:
return "31169fedc9a20ecf",
"d96adeefaa0102a9",
o = f()(n()(e)),
t.next = 5,
i.encrypt("31169fedc9a20ecf", "d96adeefaa0102a9", o);
case 5:
return r = t.sent,
t.abrupt("return", r);
case 7:
case "end":
return t.stop()
}
}), t, i)
})))()
},
buyFlag: function(e) {
var i = this;
return c()(a.a.mark((function t() {
var o;
return a.a.wrap((function(t) {
for (;;) switch (t.prev = t.next) {
case 0:
return o = {
id: e,
timestamp: Date.parse(new Date)
},
t.t0 = i.$http,
t.t1 = i.url + "/buyflag",
t.t2 = o,
t.next = 6,
i.makeToken(o);
case 6:
t.t3 = t.sent,
t.t4 = {
token: t.t3
},
t.t5 = {
headers: t.t4
},
t.t6 = function(e) {
i.$Modal.info({
title: "购买结果",
content: e.data[0].flag
})
},
t.t0.post.call(t.t0, t.t1, t.t2, t.t5).then(t.t6);
case 11:
case "end":
return t.stop()
}
}), t, i)
})))()
}
},
这段是加密函数,加密使用了aes加密,转到调用函数makeToken寻找key和iv
可以得知加密使用的key和iv,但传入加密函数的并非传入生成函数的参数e,而是经过f和n函数处理得到的结果o。
但此时已经可以根据获得的key和iv对token进行初步解密了。解密后是一串长度为32的字符,基本可以推断这是一些信息的md5加密结果,与发送的json信息的MD5值相同。
回到renderer.js,module结构如下:
module.exports = function(e){...}([function(e){}...]);
这一格式为js的IIFE函数,这种函数在定义处便执行,其中的变量不可从外部访问。我们从他的定义函数中寻找未知的函数n
此函数实现的js引擎中的stringify基础功能的一部分,用于解析字符串。结合请求体,可推断处调用的函数为toString(),不过此处并不重要,我们先前已经得知使用了md5加密。
对照得知,传入makeToken函数的是请求体,且timestamp的形成过程可知
token是由MD5,加密过的带timestamp的请求体后使用aes加密(aes-128-cbc)得到的
所以我们要做的就是利用key和iv伪造这个token,但直接传值id=3会被禁止购买
加上允许购买的id值绕过限制 “id”:“3||1” 或者 “id”:“3or2” 道理一样的
有时候可能因为网络延迟的问题,timestamp没对上,token就不对了
Exp
# -*- coding: utf-8 -*-
import datetime
import hashlib
import time
from binascii import b2a_hex
import requests
from Crypto.Cipher import AES
def plaintext(text):
#PKCS5Padding
BLOCK_SIZE = 16
pad = lambda s: (s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * chr(BLOCK_SIZE - len(s) % BLOCK_SIZE))
raw = pad(str(text))
return raw
def encrypt_aes(text, key, iv):
#加密方式:aes-128-cbc
#padding : PKCS5
cipher = AES.new(key, AES.MODE_CBC,iv)
result_aes = cipher.encrypt(plaintext(text))
result_aes = str(b2a_hex(result_aes))
return result_aes
def timestamp():
dtime = datetime.datetime.now()
timestamp = str(int(time.mktime(dtime.timetuple())))
return timestamp
def md5_encode(dataJSON):
md5_encode=hashlib.md5(dataJSON.encode(encoding='UTF-8')).hexdigest()
return md5_encode
def payload(token):
headers = {
'token': token,
'Content-Type': 'application/json;charset=UTF-8',
'Connection': 'keep-alive',
'Accept': 'application/json, text/plain, */*'
}
return headers
if __name__ == "__main__":
key = "31169fedc9a20ecf"
iv = "d96adeefaa0102a9"
url = "http://68d1a0b7-c517-4eee-847f-7ec5f4931a80.node3.buuoj.cn/buyflag"
dataJSON = '{"id":"3||2","timestamp":' + timestamp() + '000}'
#print dataJSON
token = encrypt_aes(md5_encode(dataJSON), key, iv)
r = requests.post(url, data=dataJSON, headers=payload(token))
print r.content.decode("utf-8")
print r.headers
https://glotozz.github.io/2020/05/24/gkctf-wp/#node-exe
https://blog.csdn.net/weixin_43784056/article/details/106479151?fps=1&locationNum=2