MIsc
IRC checkin
tpyx
pix
read file
PWN
once
babypwn
d
gundam
Web
upload
Python's revenge
Reverse
hacku
Crypto
base
Mobile
multicheck
ivysimple
IRC checkin
签到题,加入IRC flag写在公告里就行了
HITBXCTF{W3lcome_To_HITBXCTF_2018_Online_Qualifications}
tpyx
拿到图片第一反应是丢进Stegsolve发现打不开,用winhex打开通过观察发现是IDAT的length存在问题,对长度进行修复。然后使用binwalk分析
发现存在一个zlib,对其进行分离得到两个新文件
继续对29进行分析发现还存在一个zlib,继续分离
得到两个新文件
打开28D28D,发现里面存在一串十六进制,通过分析发现这是一个7z文件,用winhex构造7z文件
密码写在注释里面,解压得到flag
HITB{0c88d56694c2fb3bcc416e122c1072eb}
pix
下载后得到文件: aee487a2-49cd-4f1f-ada6-b2d398342d99.SteinsGate
执行file命令,发现是一个png图片
用pngcheck检查执行,没发现错误,binwalk一下也没任何发现,就直接用stegslove打开,选择数据导出:
保存后用file命令查看一下,如下:
通过以上关键字搜索到如下信息:
将从图片中导出的文件重命名为1.kdbx,通过以下链接https://fileinfo.com/extension/kdbx下载keepass,打开1.kdbx,发现需要输入密码,由hint可知密码为hint+6位数字
参考如下链接:https://bbs.ichunqiu.com/thread-36593-1-1.html 生成了相应的字典进行爆破,得到密码为hitb180408
hashcat64.exe -a 0 -m 13400 c:UserswangztDesktoppix.hash
c:UserswangztDesktopsuperdic1.txt --forcehashcat64.exe -a 0 -m 13400 c:UserswangztDesktoppix.hash
c:UserswangztDesktopsuperdic1.txt --force --show
read file
echo $input | grep -o "[[:punct:]]*"
从表面上看是只显示标点符号,但当输入$$时,输出了pid:
由此可以判断他实际过滤的是输入,即只能输入标点符号,利用通配符寻到了flag的位置:
但不能用直接输入cat之类的,于是用相同方法找到/bin/cat位置,用$()执行里面的命令,
最后payload:
$(~/../../???/??? ~/../????????????/????.???)
在文件底部找到flag:
once
程序主体
程序实现了一个双链表,允许改变一次链表节点 ,选项4进入子选项允许申请一次非fastbin堆块,允许写入一次,释放一次。
漏洞:
当输入选项不符合要求是会打印puts地址
一次输入一次
双链表解连时存在一次任意内存写,内容不可控。
漏洞利用
首先通过打印puts获得libc地址
通过一次内存任意写改global_max_fast,
此时申请堆块大小都会被计算为fastbin,寻找fastbin时能够得到libcdata段地址,通过改写file vtabl getshell
最终脚本
Babypwn
fmt盲pwn
通过格式串dump bin文件
修复ida解析得到printf got表
通过对比发现libc和once相同,格式串改写printf got getshell
最终脚本
d
程序主体
实现功能
puts("1. read message"); puts("2. edit message"); puts("3. wipe message"); puts("4. exit");
漏洞
存在单字节溢出
利用
通过单字节溢出漏洞制造unlink拿到指向bss自身指针
通过指针更改got内容完成info leak 和getshell
输入长度由strlen控制需要将strlen改为alarm_plt+6使输入长度变大
最终脚本
gundam
程序主体
结构体 struct { int inuse; char* name; char type[0x18] }
程序在bss段保存列表和计数
libc版本位2.26
漏洞
在选项3删除时bss列表中的指针没有清空,可以double free
利用
通过多次删除堆块,满足tcahce的上限(7),将堆块放入arena的bin中得到libc中得地址,完成地址泄露
利用tcache实现任意地址写
最终脚本
Upload
经过扫描开放的web端口的指纹,得知服务器为windows,IIS7,php。这里考
察的是windows下<<符号可以,用于替代路径上的未知字符。
1.上传test.pHp
2.访问
http://47.90.97.18:9999/pic.php?filename=default.jpg
3.使用绕过gd库的脚本生成图片马,再次上传,一样有出现 width和height。
有上述步骤确认,可以上传为后缀为pHp,文件,目录与default.jpg 同级。
于是开始爆破default.jpg 所在目录。
得知
wwwroot/87194f13726af7cee27ba2cfe97b60df/default.jpg
访问第一次上传的pHp
使用kali自带的weevely生成php后门,可getshell
Python's revenge
这里考察的就是pickle反序列化的问题,我们可以控制反序列化的内容,就可
以直接构造__reduce__
魔术方法任意命令执行
但这里加了一个hook装饰器对callback进行过滤,然后用的是黑名单过滤,发
现可以用map函数去绕过黑名单的限制
整理一下大概思路就两步,关键主要是黑名单绕过吧:
访问
/reminder
生成location, 本地爆破secret,得到secret : hitb利用map函数绕过黑名单任意命令执行, 构造恶意的location, 访问首页即可触发反序列化漏洞
没有回显的命令执行,直接用weblog方式带出来
Hacku
解压缩包得到一个流量包和一个chm
Chm里很显然包含着后门执行程序
通过hh.exe可以将其解包,在doc.htm中发现了后门程序的执行
通过powershell隐蔽执行了一段b64,解密后发现是一段Unicode
又是一段b64,解b64后又用了DeflateStream解压缩
查了一下zlib什么的乱七八糟很麻烦,干脆直接去掉IEX即可
得到一段脚本
观察发现通过nslookup对x.hacku.org发起解析,返回的字符串解b64后执行
于是去流量包里找DNS
将流量包dump出来并清洗后解析,得到下一段脚本
仅混淆了函数名,通读一遍后标记即可
逻辑为向cc.php发包,再解析返回的包来执行指定的命令
发包和收包都是先b64,再逐字符加密,再b64一次
于是分别写出发包和收包的解密函数,从流量包中dump出data,然后解密
Recv中可以看到data有两种,指定传输moliboom文件和下载并执abs_stage3
Moliboom的发包中可以解出path和txt,在fix后的数据中找到flag的前半段
Abs_stage3返回的字符串被ps执行了,所以显然也是脚本
但是混淆很严重,包括大小写混杂、逆序、replace等
粗看一遍大概能发现是写入了某个exe并执行,然后删除
由于是exe,因此一定会落地在磁盘里,在ISE中调试即可捕捉到
在执行前断住,然后通过路径和文件名去找这个文件
抓住以后进行反编译
值得一提的是,sub_401050是个花指令控制函数,通过堆栈的操作使得ret跳到[eax]个字节之后的地址。需要手动/脚本将其后的
花指令nop掉才能使IDA正常反编译
程序开启了磁盘的交互
对主扇区的512和1024处进行了写入
这里是MBR(主引导程序)的地址,因此实际上是个勒索病毒吧233
分别将418DB0和4187b0dump下来
查了一下,MBR是直接送给CPU执行的汇编程序,因此同样按照x86反汇编
Dump下来以后用IDA打开即可查看
注意MBR是16bit模式
16bit模式无法使用F5,所以只能生啃汇编
好消息是MBR的空间有限,程序不会太过复杂
猜测应该是简单的加密以后直接对硬编码进行cmp
因此通过查找cmp可以在一定程度上加快速度
里面的读写调用都是通过int 10/13/16来执行的,具体方法跟ax参数有关
不进行动态调试的话难度有点大,因为没法准确锁定读写的函数
Sub_0x457是input函数,循环接受最多16个字符,值得一提的是没有退格,输入一个算一个。或者遇到0x0d,即回车也会提前结束
Sub_0x494是check函数,对input进来的内容进行一些变换,然后与硬编码cmp
主要逻辑在这里
做题的时候最好还是动态调试,因为猜逻辑太难了233
动态调试主要参考52论坛的@willJ的方法
https://www.52pojie.cn/thread-173889-1-1.html
在此附加一些个人经验,和对本题的特殊之处
首先要重新启动一个ida,在进入界面选择“GO”
直接附加会内存指令无法正常显示
因为MBR的入口点在0x7c00处,该dump程序会自动被装载到0x7c00起点的地方。
自己用汇编程序编译出来的MBR似乎会自动将入口点放在合适的0x7c00,使得动态调试直接命中。
但是dump下来的idb起点是0x00,也许前面放上一些00使其偏移过去也行?
按照文章附加上以后在0x7c00处下断,此时该处为空值,无视它继续下断,再F9即会断下
单步运行到check函数中
发现ax中放着输入的字符,先ror3,sub_4df是花指令,无视即可,再依次执行xor0x74, rol5,add0x47
最后按照最低位为0/1来-1/+1
之后与bx+141处的值cmp,全部dump下来后逆向求解即可
脚本:
r = [37, 161, 57, 137, 166, 157, 213, 165, 117, 141, 74, 146, 241, 89, 94, 145]
a = [i+1 if(i&1) else i-1 for i in r]
for i in range(16):
print(chr(rol(ror((a[i]-0x47)&0xff, 5)^0x74, 3)), end='')
cmp成功以后会输出一个”}n”来补齐
与之前流量包中的前半段flag合并后提交完成
Flag:
HITBXCTF{W0rk_1n_th3_dark_ Tu_s4v@thr#1Ig&q}
Base
这个一开始没思路,然后就开始爆破,最终爆破出flag。但是题目提示了不用爆破,显然我们使用了非预期算法。
Multicheck
反编译发现load了"libcheck.so",但是没有导入native函数
反射了com.a.Check类中的check方法,这个类暂时没有发现
顺手瞟一眼claz.dex,逐字符比较了一个看起来像是flag但显然不是flag的字符串
出题师傅的恶趣味真是~
于是Java层中暂时没什么线索了,反编译so看看
在init_array中发现了一个函数,跟过去往里看
Sub_1380里有一些令人在意的东西
新建了一个文件,往里写了一些内容
把byte_4004里的值异或后dump下来
打开发现是个dex,反编译得到com.a.check类
Check方法里将arg和硬编码b进行了比较
然后一路跟着a进去,是4个同名不同参的重载函数
先整理了个数,将bytes后移,第一个byte存储偏移,之后补0
然后每8个byte一组,转成两个int(大端序)
然后是核心变换,与之前强网杯的hide算法相同
32轮迭代,每轮先对key自增,然后互相运算
逆起来很简单,按着逻辑先把v1自增32次,然后将v2自减,再将v3自减,最后v1自减即可
然后有一个关键问题就是,之前的hide是elf格式的,而这次在安卓中的jvm中执行,写了python脚本怎么跑都不对
由于是不同的dex,jeb也跟不进反射的方法,Xp hook的话又太麻烦。
最后突发奇想复制到java环境里执行,果然得到了正确的逻辑:
Java中>>为带符号的右移,要考虑各个参数的正负,如果是负数>>时应补1。
Python调了半天都没修正好,默认的int是无限宽度的,必须用0xffffffff去修正,而这会导致符号丢失。
调了一万年以后突然想起来何苦非要用python写脚本呢,遂将java的check类逻辑小改,得到flag
解码脚本:
import java.util.Arrays; public class HelloWorld { public static void main(String[] args) { int i; byte[] t = { 99, 124, 101, -23, -114, 81, -47, -39, -102, 79, 22, 52, -39, -94, -66, -72, 101, -18, 73, -27, 53, -5, 46, -20, 97, 11, -56, 36, -19, -49, -112, -75 }; byte[] tt = re(t); for(i=0;i<tt.length;i++) { System.out.print((char)(tt[i]&0xff)); /*System.out.print(", "); if((i+1)%8==0 && i!=0) { System.out.println(); }*/ } } private static int[] a = { -1414812757, -842150451, -269488145, 305419896 }; private static byte[] b = { 99, 124, 101, -23, -114, 81, -47, -39, -102, 79, 22, 52, -39, -94, -66, -72, 101, -18, 73, -27, 53, -5, 46, -20, 97, 11, -56, 36, -19, -49, -112, -75 }; private static byte[] a(int[] paramArrayOfInt, int paramInt) { byte[] arrayOfByte = new byte[paramArrayOfInt.length << 2]; int i = 0; while (paramInt < arrayOfByte.length) { arrayOfByte[(paramInt + 3)] = ((byte)(paramArrayOfInt[i] & 0xFF)); arrayOfByte[(paramInt + 2)] = ((byte)(paramArrayOfInt[i] >> 8 & 0xFF)); arrayOfByte[(paramInt + 1)] = ((byte)(paramArrayOfInt[i] >> 16 & 0xFF)); arrayOfByte[paramInt] = ((byte)(paramArrayOfInt[i] >> 24 & 0xFF)); i += 1; paramInt += 4; } return arrayOfByte; } private static int a(byte paramByte) { int i = paramByte; if (paramByte < 0) { i = paramByte + 256; } return i; } private static int[] b(byte[] paramArrayOfByte, int paramInt) { int[] arrayOfInt = new int[paramArrayOfByte.length >> 2]; int i = 0; while (paramInt < paramArrayOfByte.length) { arrayOfInt[i] = (a(paramArrayOfByte[(paramInt + 3)]) | a(paramArrayOfByte[(paramInt + 2)]) << 8 | a(paramArrayOfByte[(paramInt + 1)]) << 16 | paramArrayOfByte[paramInt] << 24); i += 1; paramInt += 4; } return arrayOfInt; } public static byte[] re(byte[] paramString) { return re_a(paramString); } public static byte[] re_a(byte[] arrayOfByte) { /*int i = 8 - paramArrayOfByte.length % 8; byte[] arrayOfByte = new byte[paramArrayOfByte.length + i]; arrayOfByte[0] = ((byte)i); System.arraycopy(paramArrayOfByte, 0, arrayOfByte, i, paramArrayOfByte.length);*/ int i; byte[] paramArrayOfByte = new byte[arrayOfByte.length]; i = 0; while (i < paramArrayOfByte.length) { System.arraycopy(re_c(arrayOfByte, i, a, 32), 0, paramArrayOfByte, i, 8); i += 8; } return paramArrayOfByte; } static byte[] re_c(byte[] paramArrayOfByte, int paramInt1, int[] paramArrayOfInt, int paramInt2) { int[] ArraryInt = b(paramArrayOfByte, paramInt1); int i = ArraryInt[0]; paramInt1 = ArraryInt[1]; //System.out.println(Integer.toHexString(i)); //System.out.println(Integer.toHexString(paramInt1)); int k = 0; int m = paramArrayOfInt[0]; int n = paramArrayOfInt[1]; int i1 = paramArrayOfInt[2]; int i2 = paramArrayOfInt[3]; int j = 0; while(j<paramInt2) { k -= 1640531527; j += 1; } while (j > 0 ) { paramInt1 -= ((i << 4) + i1 ^ i + k ^ (i >> 5) + i2); i -= ((paramInt1 << 4) + m ^ paramInt1 + k ^ (paramInt1 >> 5) + n); /*System.out.print((paramInt1)); System.out.println("t");*/ k += 1640531527; j -= 1; } ArraryInt[0] = i; ArraryInt[1] = paramInt1; return a(ArraryInt, 0); } }
Kivysimple
先查了一下,发现kivy是python开发apk的套件
那么很显然,这种套件的逻辑应该是在安卓中安装python虚拟机,然后将py编译成pyc/pyo来执行
扫了一圈assest和lib,没有发现pyo
不过lib中看到了python27.so,基本可以确定我们的判断
Libmain.so这个名字很吸引人,进去看一眼
查字符串,发现
都是python的原始代码,也就是说虚拟机确实在安卓中解释执行代码了
虽然还是没有发现程序的代码,但是注意到有很多自带的log,于是进行安装运行尝试
发现虚拟机在这个路径里,过去看看
一下就抓到了关键,将它pull出来并反编译
出题师傅的恶趣味真是~x2
显然关键在b64解出来的代码里,于是将eval去掉,查看code object的信息
PS:由于源代码为python2所写,因此必须使用python2来解码。Python3的marshal格式不同,会报错。
可以看到有一个check函数,也是code object
先通过dis模块来解析字节码
啥都没有╮(╯_╰)╭就是调用了check函数而已
于是再解析check
还好没有很复杂的结构,基本上看单词和值就能大差不离的猜出来
s = flag if(len(s)!=31): return False if(s[17]!=’7’): jmp 54 …
下面都是类似的结构
因此只要把对应的下标和值提取出来即可得到flag
数据清洗使用正则匹配,或者手动复制都可233
得到flag:
HITB{1!F3_1S_&H%r7_v$3_pY7#ON!}
本文作者:白帽100安全攻防实验室
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/71257.html