whaleCTF-30days-隐写【第二期】-彩虹糖-writeup

题目:

彩虹糖公司的机器出了问题,不断的在他们的宣传海报里插入彩虹糖,最后公司发现,插入的竟然是公司机密,请帮助他们找到所有机密。答案格式whaleCTF{xxx}

打开压缩包发现6张图片,放大图片能看到很多彩色小点,也就是题目中说的彩虹糖,仔细观察其他图片也都有小点。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第1张图片

使用binwalk、stegsolve、winhex、strings等都没有发现有用的信息,只发现如下内容:

sec@LAPTOP-O988M64E:/mnt/f/whaleCTF/隐写【第二期】/彩虹糖【初级】$ strings neoncow.bmp
,$this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: this is not the flag you're looking for, but keep looking!! :: 

已经没有思路了,经大神指点,考虑图片的像素,使用PIL库将图片中的彩虹糖(也就是彩色的小点提取出来),这里可以使用这两张像素颜色较少的图片进行提取:

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第2张图片

我们使用neoncow.bmp这张图片,这张图片中包含了绿、黄、粉、紫、黑、白六中颜色,使用取色器依次将这六种颜色的16进制RGB值取出来。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第3张图片

接下来,使用python遍历图片,把这六种颜色去除:

from PIL import Image
import operator
import pprint

img = Image.open("neoncow.bmp")

colors = {}

pixels = img.load()
w,h = img.size

for y in range(h):
    for x in range(w):
        if pixels[x,y] not in [
                (0xfd, 0xfd, 0xfd),
                (0x07, 0x90, 0x06),
                (0xfd, 0x00, 0xeb),
                (0xfd, 0xff, 0x24),
                (0x96, 0x3c, 0xfd),
                (0x24, 0x2c, 0x03),
            ]:

            pixels_repr = ''.join(["%02x" % _ for _ in pixels[x,y]])
            if pixels_repr not in colors:
                colors[pixels_repr] = 0
            colors[pixels_repr] += 1

pprint.pprint(sorted(colors.items(), key=operator.itemgetter(1))[::-1])

运行程序即可提取像素内容:

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第4张图片

仍然没有思路,小白表示很无助,还得大神指点,观察压缩包文件名,叫ximage.zip,学名是execimage,脑洞不够大呀,完全想不到,考虑这个一个可执行的图片,于是和机器代码联系起来,发现在红色通道出现多处90,在蓝色通道出现多处80,先通过一个在线的反编译工具对90c031进行测试:

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第5张图片

发现结果并不是很对,考虑会不会是倒序存储,尝试将RGB改为BGR,及对31c090进行反编译

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第6张图片

出现了异或运算,说明思路应该没有错。

使用python脚本capstone库,对每段代码进行反汇编:

from capstone import *

from PIL import Image

md = Cs(CS_ARCH_X86, CS_MODE_32)

img = Image.open("neoncow.bmp")

pixels = img.load()
w,h = img.size

s = ""
for y in range(h):
    for x in range(w):
        if pixels[x,y] not in [
                (0xfd, 0xfd, 0xfd),
                (0x07, 0x90, 0x06),
                (0xfd, 0x00, 0xeb),
                (0xfd, 0xff, 0x24),
                (0x96, 0x3c, 0xfd),
                (0x24, 0x2c, 0x03),
            ]:
                s += ''.join([chr(_) for _ in pixels[x,y][::-1]])

for inst in md.disasm(s, 0):
    print "0x%x:\t%s\t%s" % (inst.address, inst.mnemonic, inst.op_str)

 发现代码中都是对ecx进行赋值和加减,并且每次计算结束都调用系统中断输出,最后输出了ecx的内容。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第7张图片whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第8张图片

将输出结果重定向到sam.out文件,再对ecx的值进行计算,代码如下:

f = open("sam.out")

lines = f.readlines()

s = ""
ecx = 0

for l in lines[::-1]:  # reverse the instructions
    if 'byte ptr [ecx]' in l:
        tmp = l.strip().split(', ')[1]
        if tmp.startswith("0x"):
            tmp = int(tmp, 16)
        else:
            tmp = int(tmp)

        if 'add' in l:
            ecx += tmp
        elif 'sub' in l:
            ecx -= tmp

    elif 'int\t0x80' in l:
        s += chr(ecx)

print s

运行代码后发现有点小问题。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第9张图片

仔细观察发现其他图片也有类似的彩虹糖小点,换一张图片试试,这次我们只需提取RGB中红色为0x90和蓝色为0x80的像素点即可:

from capstone import *

from PIL import Image

md = Cs(CS_ARCH_X86, CS_MODE_32)

img = Image.open("happycow.bmp")

pixels = img.load()
w,h = img.size

s = ""
for y in range(h):
    for x in range(w):
        if pixels[x,y][0]==0x90 or pixels[x,y][2]==0x80:
                s += ''.join([chr(_) for _ in pixels[x,y][::-1]])

for inst in md.disasm(s, 0):
    print "0x%x:\t%s\t%s" % (inst.address, inst.mnemonic, inst.op_str)

将输出结果重定向到nappycow.out,再对结果进行计算,发现了flag,但是提交并不对。尝试其他图片也都不对,╮(╯▽╰)╭,三次登门请教大神。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第10张图片

经提示,BPM图片的存储方式(像素点映射方式)是按照从左到右,从下到上!所以取出来的点都是反的,于是重新取点:

f = open("happycow.out")

lines = f.readlines()

s = ""
ecx = 0

for l in lines:
    if 'byte ptr [ecx]' in l:
        tmp = l.strip().split(', ')[1]
        if tmp.startswith("0x"):
            tmp = int(tmp, 16)
        else:
            tmp = int(tmp)

        if 'add' in l:
            ecx += tmp
        elif 'sub' in l:
            ecx -= tmp

    elif 'int\t0x80' in l:
        s += chr(ecx)

print s

得到了不一样的FLAG,提交发现flag正确。

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第11张图片

再次对第一张图片进行计算验证,发现得到了相同的FLAG

whaleCTF-30days-隐写【第二期】-彩虹糖-writeup_第12张图片 

对六张图片一次进行计算验证:

from capstone import *
import glob
from PIL import Image

md = Cs(CS_ARCH_X86, CS_MODE_32)

for fn in glob.glob("*.bmp"):

    img = Image.open(fn)
    pixels = img.load()
    w,h = img.size

    s = ""
    for y in range(h-1, -1, -1):
        for x in range(w):
            if pixels[x,y][0] == 0x90 or pixels[x,y][2] == 0x80:
                s += ''.join([chr(_) for _ in pixels[x,y][::-1]])

    lines = []
    for inst in md.disasm(s, 0):
        lines += ["0x%x:\t%s\t%s" % (inst.address, inst.mnemonic, inst.op_str)]

    print fn

    ecx = 0
    out = ""
    for l in lines:
        if 'byte ptr [ecx]' in l:
            tmp = l.strip().split(', ')[1]
            if tmp.startswith("0x"):
                tmp = int(tmp, 16)
            else:
                tmp = int(tmp)

            if 'add' in l:
                ecx += tmp
            elif 'sub' in l:
                ecx -= tmp

        elif 'int\t0x80' in l:
            out += chr(ecx)

    print out

发现结果都一样,得到flag!

λ py2 cal-all.py
660px-San_Francisco_districts_map.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***


alcatraz.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***


angry_cow.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***


fireescape.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***


happycow.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***


neoncow.bmp
***
FLAG:c3dbbf0298eceb3edcd6d2505fd8d30d
***

非常不容易,通过这道题,学到了不少,总结一下新的姿势,知识点如下:


Capstone:

做pwn题经常使用,功能强大的汇编处理库,本题用到的是将16进制转换为汇编程序。

from capstone import *
shellcode = b"\x31\xc0\x90"
md = Cs(CS_ARCH_X86,CS_MODE_32)
for i in md.disasm(shellcode,0x00):
    print "0x%x:\t%s\t%s" % (i.address, i.mnemonic, i.op_str)

shellcode = b"\x31\xc0\x90":定义16进制shellcode

md = Cs(CS_ARCH_X86,CS_MODE_32):初始化类并给出两个参数(硬件架构和硬件模式),本题中,我们反汇编的是x86体系结构的32位代码

for i in md.disasm(shellcode,0x00): disasm 反汇编16进制代码,其参数是shellcode和起始地址。

print “0x%x: \ t%s \ t%s”%(i.address, i.mnemonic, i.op_str):输出地址,操作指令和操作数。

 

 

 

 

你可能感兴趣的:(CTF,隐写)