2021医疗行业ctf初赛wp

WEB

签到

打开网页发现是一个小游戏,直接查看源代码搜索 flag

super_hacker

根据题目提示,X-Forwarded-For 处存在 smarty 模板注入漏洞

访问 url/index.php 添加 Via 头和 XFF 头进行测试,发现存在 smarty 模板注入漏洞

图片[1]-2021医疗行业ctf初赛wp-魔法少女雪殇

尝试执行 phpinfo()函数,发现存在过滤,经过测试发现过滤了 i、I 和空格

图片[2]-2021医疗行业ctf初赛wp-魔法少女雪殇

使用\x20 配合 system()函数可以绕过空格限制

图片[3]-2021医疗行业ctf初赛wp-魔法少女雪殇

尝试写入一句话木马查找 flag

图片[4]-2021医疗行业ctf初赛wp-魔法少女雪殇
图片[5]-2021医疗行业ctf初赛wp-魔法少女雪殇

使用 find / -name flag 命令查找 flag 并读取

图片[6]-2021医疗行业ctf初赛wp-魔法少女雪殇

easy_web1

打开题目链接,发现内容为 flag 在哪

f12,指向一个php

图片[7]-2021医疗行业ctf初赛wp-魔法少女雪殇

访问,发现需要本地 ip 才能访问,添加 http 头进行绕过,成功读取到 flag

图片[8]-2021医疗行业ctf初赛wp-魔法少女雪殇

easy_web2

题目代码如下

图片[9]-2021医疗行业ctf初赛wp-魔法少女雪殇

cthshow的原题,百度直接打就行了

MISC

drawing

一堆 dcm 文件,用 RadiAnt DICOM Viewer 打开导入

图片[10]-2021医疗行业ctf初赛wp-魔法少女雪殇

可以进行播放,更改模式

图片[11]-2021医疗行业ctf初赛wp-魔法少女雪殇

在 im:301 处发现纯黑页面,然后进行调整为 full dynamic

图片[12]-2021医疗行业ctf初赛wp-魔法少女雪殇
图片[13]-2021医疗行业ctf初赛wp-魔法少女雪殇

CRYPTO

base 编码

R1kzRE1RWldHRTNET04yQ0dZWkRNTUpYR00zREtNWldHTTJES1JSVEdNWlRFTktHR01ZVEdOUlZJWTNES05SUkc0WlRPT0pWSVkzREVOUlJHNFpUTU5KWElRPT09PT09

直接 base64-base32-base16 就完事了

REVERSE

longlong

根据字符串判断是go文件,使用IDAGolangHelper

然后插件出现问题,可以在插件的Utils.py中将name转为str

if type(name)==bytes:
    name=str(name,encoding='utf-8')

重命名之后进入main_main

f5时总是call analysis failed,把错误处改为nop即可

call    runtime_morestack_noctxt

大概流程是一种虚拟机,先编译程序,再运行程序

编译流程

每一个字符形成一个dword

[:6 0
]:7 0
<:1 0
>:0 0
-:3 0
.:4 0
+:2 0
,:5 0

实际上直接调试获得编译后的代码就可以

执行流程

固定长度指令,第一个字为操作数,第二个字为操作码

几个重要变量:

v25:指令
v18:如果v25!=0就++,=0就--
v57:v18
v14:指令起始地址
i:指令计数器
v16:一些标志位

各个指令定义

6:jz 地址
7:jg 地址
4:输出
5:输入
3:x--
2:x++
1:调整v18--
0:调整v18++

解题过程

根据这些定义可以写出反汇编器

# 导入指令
data=open('1.txt').read()
cmds=[]
for line in data.splitlines():
    for w in line.split(' '):
        if w and not ':' in w:
            cmds.append(int(w[2:],base=16))
print(cmds)
# 指令长度
assert len(cmds)==0x947
to_file=''
​
for i,cmd in enumerate(cmds):
    opc=cmd&0xffff
    opd=cmd>>16
    s='unknown'
    if opc==0:
        s='v18++'
    elif opc==1:
        s='v18--'
    elif opc==2:
        s='v16[v18]++'
    elif opc==3:
        s='v16[v18]--'
    elif opc==4:
        s='output'
    elif opc==5:
        s='input'
    elif opc==6:
        s='jz 0x%x'%(opd+1)
    elif opc==7:
        s='jg 0x%x'%(opd+1)
    p_s='0x%02x:\t%s'%(i,s)
    to_file+=p_s+'\n'
    print(p_s)
with open('out.log','w') as f:
    f.write(to_file)

得到反汇编指令后,发现共2547条指令,太庞大了

通过观察可以发现,有几种精简方法

  1. 连续的v16[v18]++,可以简化为v16[v18]+=cnt
  2. 连续的v16[v18]–,v18++,v18–同上
  3. v16[v18]–,jg上一条指令:这个是清零指令,可以精简。连同当前指令的上一条jz。。。也可以精简
# 导入指令
data=open('1.txt').read()
cmds=[]
for line in data.splitlines():
    for w in line.split(' '):
        if w and not ':' in w:
            cmds.append(int(w[2:],base=16))
print(cmds)
# 指令长度
assert len(cmds)==0x947
to_file=''
i=0
delta=0
while i<len(cmds):
    cmd=cmds[i]
    opc=cmd&0xffff
    opd=cmd>>16
    s='unknown'
    delta=1
    if opc==0:
        s='v18++'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==0:
                cnt+=1
            else:
                break
        s='v18+=%d'%cnt
        delta+=cnt-1
    elif opc==1:
        s='v18--'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==1:
                cnt+=1
            else:
                break
        s='v18-=%d'%cnt
        delta+=cnt-1
    elif opc==2:
        s='v16[v18]++'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==2:
                cnt+=1
            else:
                break
        s='v16[v18]+=%d'%cnt
        delta+=cnt-1
    elif opc==3:
        s='v16[v18]--'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==3:
                cnt+=1
            else:
                break
        s='v16[v18]-=%d'%cnt
        delta+=cnt-1
        
    elif opc==4:
        s='output'
    elif opc==5:
        s='input'
    elif opc==6:
        s='jz 0x%x'%(opd+1)
        opc1=cmds[i+1]&0xffff
        opd1=cmds[i+1]>>16
        opc2=cmds[i+2]&0xffff
        opd2=cmds[i+2]>>16
        if opc2==7 and opd2==i and opc1==3:
            delta+=3-1
            s='v16[v18]=0'
    elif opc==7:
        s='jg 0x%x'%(opd+1)
    p_s='0x%03x:\t%s'%(i,s)
    to_file+=p_s+'\n'
    print(p_s)
    i+=delta
with open('out.log','w') as f:
    f.write(to_file)
​
i=0
v16=[0]*5
v18=0
over=0
while i<len(cmds):
    cmd=cmds[i]
    opc=cmd&0xffff
    opd=cmd>>16
    s='unknown'
    delta=1
    if opc==0:
        s='v18++'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==0:
                cnt+=1
            else:
                break
        s='v18+=%d'%cnt
        delta+=cnt-1
        v18+=cnt
    elif opc==1:
        s='v18--'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==1:
                cnt+=1
            else:
                break
        s='v18-=%d'%cnt
        delta+=cnt-1
        v18-=cnt
    elif opc==2:
        s='v16[v18]++'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==2:
                cnt+=1
            else:
                break
        s='v16[v18]+=%d'%cnt
        delta+=cnt-1
        v16[v18]+=cnt
    elif opc==3:
        s='v16[v18]--'
        cnt=0
        for j in range(i,len(cmds)):
            opc=cmds[j]&0xffff
            if opc==3:
                cnt+=1
            else:
                break
        s='v16[v18]-=%d'%cnt
        delta+=cnt-1
        v16[v18]-=cnt
    elif opc==4:
        s='output'
        print(chr(v16[v18]))
    elif opc==5:
        s='input'
    elif opc==6:
        s='jz 0x%x'%(opd+1)
        opc1=cmds[i+1]&0xffff
        opd1=cmds[i+1]>>16
        opc2=cmds[i+2]&0xffff
        opd2=cmds[i+2]>>16
        if opc2==7 and opd2==i and opc1==3:
            delta+=3-1
            s='v16[v18]=0'
            v16[v18]=0
        else:
            if v16[v18]==0:
                i=opd
    elif opc==7:
        s='jg 0x%x'%(opd+1)
        if v16[v18]>0:
            i=opd
    p_s='0x%03x:\t%s'%(i,s)
    to_file+=p_s+'\n'
    # print(p_s)
    i+=delta
    print(i,v18,v16)

最后得到1317条指令

发现v18最大可以取到4,所以v16是寄存器组

根据提示brainfuck,找到个可视化执行工具

https://github.com/fatiherikli/brainfuck-visualizer

通过可视化工具可以知道该程序判断的方法,即输入一个字符后判断是否相等,按字符比较的。所以重点查看输入后的部分,搜索反汇编代码中的”input”关键字

对于每一个输入的字符,都和计算出的字符比较。以第一个字符为例进行分析

0x0e5:  input ; v16[v18]=input,此时v18=2
0x0e6:  v18+=1
0x0e7:  v16[v18]=0 ; v16[3]=0
0x0ea:  v18+=1
0x0eb:  v16[v18]=0
0x0ee:  v16[v18]+=6 ; v16[4]=6
0x0f4:  jz 0x102
0x0f5:  v18-=1
0x0f6:  v16[v18]+=9 ; v16[3]=6*9
0x0ff:  v18+=1
0x100:  v16[v18]-=1
0x101:  jg 0xf5 ; 这里循环完成乘法
; 下面开始比较过程
0x102:  v18-=1
; 其它字符可能在这两句代码之间变化,需要注意。实际上都是a*b+c的形式
0x103:  v18+=1
0x104:  v18-=2 ; v18=2,即用户输入
0x106:  jz 0x10c
0x107:  v16[v18]-=1
0x108:  v18+=1
0x109:  v16[v18]-=1
0x10a:  v18-=1
0x10b:  jg 0x107
​
0x10c:  v16[v18]+=1
0x10d:  v18+=1
0x10e:  jz 0x116 ; 这里应该判断成功,所以v16[v18]=v16[3]=0,由此推出v16[v18]=6*9=54='6'
0x10f:  v18-=1
0x110:  v16[v18]-=1
0x111:  v18+=1
0x112:  v16[v18]=0
0x115:  jg 0x10f
; 这里记录标志位并初始化下一个
0x116:  v18-=1
0x117:  jz 0x17c
0x118:  v18+=2
0x11a:  v16[v18]=0
0x11d:  v18+=1
0x11e:  v16[v18]=0
0x121:  v18-=4
0x125:  jz 0x132
0x126:  v18+=3
0x129:  v16[v18]+=1
0x12a:  v18+=1
0x12b:  v16[v18]+=1
0x12c:  v18-=4
0x130:  v16[v18]-=1
0x131:  jg 0x126
0x132:  v18+=4
0x136:  jz 0x142
0x137:  v18-=4
0x13b:  v16[v18]+=1
0x13c:  v18+=4
0x140:  v16[v18]-=1
0x141:  jg 0x137
0x142:  v16[v18]=0
0x145:  v16[v18]+=1
0x146:  v18+=1
0x147:  v18-=2
0x149:  v18+=1
0x14a:  jz 0x150
0x14b:  v18-=1
0x14c:  v16[v18]+=1
0x14d:  v18+=1
0x14e:  v16[v18]-=1
0x14f:  jg 0x14b
0x150:  v18-=1
0x151:  v18+=1
0x152:  v16[v18]=0
0x155:  v18-=4
0x159:  v16[v18]=0
0x15c:  v18+=3
0x15f:  jz 0x16c
0x160:  v18+=1
0x161:  v16[v18]+=1
0x162:  v18-=4
0x166:  v16[v18]+=1
0x167:  v18+=3
0x16a:  v16[v18]-=1
0x16b:  jg 0x160
0x16c:  v18+=1
0x16d:  jz 0x173
0x16e:  v18-=1
0x16f:  v16[v18]+=1
0x170:  v18+=1
0x171:  v16[v18]-=1
0x172:  jg 0x16e
0x173:  v18-=1
0x174:  v18+=1
0x175:  v18-=3
0x178:  v16[v18]=0
0x17b:  jg 0x118
0x17c:  v16[v18]=0
0x17f:  v18+=1
0x180:  v18-=1

可以使用相同的方法得到另外11个字符,最终flag为

678d39028ac5

记得包裹flag

© 版权声明
THE END
喜欢就支持一下吧
点赞1 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情