深入解析Quarkslab 2014安全挑战:Python虚拟机与代码混淆技术

本文详细解析了Quarkslab 2014安全挑战,涉及Python代码混淆、自定义ELF64 Python解释器、虚拟机操作码修改、字节码反汇编与反编译技术,最终通过动态调试和静态分析获得隐藏的Flag。

深入解析Quarkslab的2014安全挑战

引言

由于博客沉寂了一段时间,我觉得写一篇文章会很酷;所以朋友们,深入挖掘吧! 法国公司Quarkslab最近发布了一个安全挑战,获胜者可以免费参加10月13日至16日在吉隆坡举行的HITBSecConf会议。 这个挑战由专注于编译器/并行计算的研发工程师Serge Guelton编写。截至撰写时,已有八人成功解决了挑战,其中一张门票似乎被hackedd赢得,祝贺他!

根据挑战描述,Python被大量使用,这至少有两个好处:

  • 首先,因为我之前已经有机会查看其源代码,
  • 其次,因为我非常喜欢Python。

在这篇文章中,我将描述如何解决这个问题,以及如何成功找到答案。为了弥补我解决速度慢的不足,我尽量详细地描述。最初打算写得很短,但不幸的是,我决定全面分析挑战,即使不需要找到密钥,所以比预期的要长一些:-)。 无论如何,请坐好,放松一下,让我在开始前给你倒杯茶:-)。

目录

  • 引言
  • 找到挑战的URL
  • 非常一行代码,很多lambda,如此痛苦
  • 整理最后一个..
  • ..然后是前一个…
  • 将所有内容整合在一起
  • 自定义ELF64 Python解释器,你需要调试
  • 侦察
  • 找到有趣的部分
  • do_not_run_me.run_me
  • 第一个marshal函数
  • 介绍dpy.py
  • 我现在需要一个反汇编器,爸爸
  • 回到第一个marshal函数,使用我们所有的工具
  • 运行我的字节码
  • 第二个和最后一个marshal函数
  • 我需要一个反编译器,爸爸
  • 结论

找到挑战的URL

非常一行代码,很多lambda,如此痛苦

挑战的第一部分是检索隐藏在以下Python一行代码中的URL:

1
(lambda g, c, d: (lambda _: (_.__setitem__('$', ''.join([(_['chr'] if ('chr' in _) else chr)((_['_'] if ('_' in _) else _)) for _['_'] in (_['s'] if ('s' in _) else s)[::(-1)]])), _)[-1])( (lambda _: (lambda f, _: f(f, _))((lambda __,_: ((lambda _: __(__, _))((lambda _: (_.__setitem__('i', ((_['i'] if ('i' in _) else i) + 1)),_)[(-1)])((lambda _: (_.__setitem__('s',((_['s'] if ('s' in _) else s) + [((_['l'] if ('l' in _) else l)[(_['i'] if ('i' in _) else i)] ^ (_['c'] if ('c' in _) else c))])), _)[-1])(_))) if (((_['g'] if ('g' in _) else g) % 4) and ((_['i'] if ('i' in _) else i)< (_['len'] if ('len' in _) else len)((_['l'] if ('l' in _) else l)))) else _)), _) ) ( (lambda _: (_. __setitem__('!', []), _.__setitem__('s', _['!']), _)[(-1)] ) ((lambda _: (_. __setitem__('!', ((_['d'] if ('d' in _) else d) ^ (_['d'] if ('d' in _) else d))), _.__setitem__('i', _['!']), _)[(-1)])((lambda _: (_.__setitem__('!', [(_['j'] if ('j' in _) else j) for _[ 'i'] in (_['zip'] if ('zip' in _) else zip)((_['l0'] if ('l0' in _) else l0), (_['l1'] if ('l1' in _) else l1)) for _['j'] in (_['i'] if ('i' in _) else i)]), _.__setitem__('l', _['!']), _)[-1 ])((lambda _: (_.__setitem__('!', [1373, 1281, 1288, 1373, 1290, 1294, 1375, 1371,1289, 1281, 1280, 1293, 1289, 1280, 1373, 1294, 1289, 1280, 1372, 1288, 1375,1375, 1289, 1373, 1290, 1281, 1294, 1302, 1372, 1355, 1366, 1372, 1302, 1360, 1368, 1354, 1364, 1370, 1371, 1365, 1362, 1368, 1352, 1374, 1365, 1302 ]), _.__setitem__('l1',_['!']), _)[-1])((lambda _: (_.__setitem__('!',[1375, 1368, 1294, 1293, 1373, 1295, 1290, 1373, 1290, 1293, 1280, 1368, 1368,1294, 1293, 1368, 1372, 1292, 1290, 1291, 1371, 1375, 1280, 1372, 1281, 1293,1373, 1371, 1354, 1370, 1356, 1354, 1355, 1370, 1357, 1357, 1302, 1366, 1303,1368, 1354, 1355, 1356, 1303, 1366, 1371]), _.__setitem__('l0', _['!']), _)[(-1)]) ({ 'g': g, 'c': c, 'd': d, '$': None})))))))['$'])

我认为这是我第一次看到混淆的Python,相信我,当我看到这个片段时,我做出了一个非常奇怪的表情。但是,只要有一点耐心,我们应该能够更好地理解它是如何工作的,让我们开始吧!

整理最后一个..

在开始之前,我们可以通过仔细观察片段直接观察到一些事情:

  • 我们知道这个函数有三个参数;不过此时我们不知道它们是什么
  • 片段似乎大量重用__setitem__;这可能对我们意味着两件事:
    • 我知道的唯一具有__setitem__函数的标准Python对象是字典,
    • 从片段的外观来看,似乎一旦我们理解了其中一个__setitem__调用,我们就会理解所有
  • 使用了以下标准函数:chr、len、zip
    • 这意味着操作字符串、整数和可迭代对象
  • 有两个明显的运算符:mod和xor

有了所有这些信息,我做的第一件事是尝试清理它,从片段的最后一个lambda开始。它给出了类似这样的东西:

1
2
tab0 = [1375, 1368, 1294, 1293, 1373, 1295, 1290, 1373, 1290, 1293, 1280, 1368, 1368,1294, 1293, 1368, 1372, 1292, 1290, 1291, 1371, 1375, 1280, 1372, 1281, 1293,1373, 1371, 1354, 1370, 1356, 1354, 1355, 1370, 1357, 1357, 1302, 1366, 1303,1368, 1354, 1355, 1356, 1303, 1366, 1371]
z = lambda x: (x.__setitem__('!', tab0), x.__setitem__('l0', x['!']), x)[-1]

这个lambda接受一个字典x,设置两个项目,生成一个元组,在元组末尾包含对字典的引用;最后,lambda将返回同一个字典。 它还使用x[’!’]作为一个临时变量,然后将其值分配给x[’l0’]。 长话短说,它基本上接受一个字典,更新它并将其返回给调用者:一个聪明的技巧,可以在lambdas之间传递同一个对象。我们也可以直接在Python中轻松看到:

1
2
3
In [8]: d = {}
In [9]: z(d)
Out[9]: {'!': [1375, ...], 'l0': [1375, ...]}

这个lambda甚至被调用时带有一个字典,该字典将包含,除其他外,三个用户控制的变量:g、c、d。 这个字典似乎是一种存储,用于跟踪将在这些lambdas中使用的所有变量。

1
2
# Returns { 'g' : g, 'c', 'd': d, '$':None, '!':tab0, 'l0':tab0}
last_res = ((lambda x: (x.__setitem__('!', tab0), x.__setitem__('l0', x['!']), x)[-1])({ 'g': g, 'c': c, 'd': d, '$': None})

..然后是前一个…

现在,如果我们对最后一个lambda之前的那个重复相同的操作,我们有完全相同的模式:

1
2
tab1 = [1373, 1281, 1288, 1373, 1290, 1294, 1375, 1371, 1289, 1281, 1280, 1293, 1289, 1280, 1373, 1294, 1289, 1280, 1372, 1288, 1375, 1375, 1289, 1373, 1290, 1281, 1294, 1302, 1372, 1355, 1366, 1372, 1302, 1360, 1368, 1354, 1364, 1370, 1371, 1365, 1362, 1368, 1352, 1374, 1365, 1302]
zz = lambda x: (x.__setitem__('!', tab1), x.__setitem__('l1', x['!']), x)[-1]

完美,现在让我们一遍又一遍地重复相同的操作。在某个时刻,整个事情变得清晰起来(有点):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Returns { 
# 'g':g, 'c':c, 'd':d,
# '!':[],
# 's':[],
# 'l':[j for i in zip(tab0, tab1) for j in i],
# 'l1':tab1,
# 'l0':tab0,
# 'i': 0,
# 'j': 1302,
# '$':None
#}
res_after_all_operations = (
    (lambda x: (x.__setitem__('!', []), x.__setitem__('s', x['!']), x)[-1])
    ((lambda x: (x.__setitem__('!', ((x['d'] if ('d' in x) else d) ^ (x['d'] if ('d' in x) else d))), x.__setitem__('i', x['!']), x)[-1])
    ((lambda x: (x.__setitem__('!', [(x['j'] if ('j' in x) else j) for x[ 'i'] in (x['zip'] if ('zip' in x) else zip)((x['l0'] if ('l0' in x) else l0), (x['l1'] if ('l1' in x) else l1)) for x['j'] in (x['i'] if ('i' in x) else i)]), x.__setitem__('l', x['!']), x)[-1])
    ((lambda x: (x.__setitem__('!', tab1), x.__setitem__('l1', x['!']), x)[-1])
    ((lambda x: (x.__setitem__('!', tab0), x.__setitem__('l0', x['!']), x)[-1])
    ({ 'g': g, 'c': c, 'd': d, '$': None})
)
)
)
)

将所有内容整合在一起

在完成所有这些之后,我们现在知道函数需要正常工作的三个变量的类型(老实说,我们不需要更多):

  • g是一个整数,将被mod 4
    • 如果值可被4整除,函数不返回任何内容;所以我们需要将这个变量设置为1,例如
  • c是另一个整数,看起来像一个xor密钥;如果我们查看片段,这个变量用于对x[’l’]的每个字节进行xor(这是带有tab0和tab1的表)
    • 这是有趣的参数
  • d是另一个整数,我们也可以忽略:它仅用于通过将x[’d’]与自身进行xor来将x[‘i’]设置为零。

我们现在真的不需要其他任何东西:没有更多的lambdas,没有更多的痛苦,没有更多的眼泪。是时候写我所谓的,一个有教养的暴力破解器,来找到c的正确值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sys

def main(argc, argv):
    tab0 = [1375, 1368, 1294, 1293, 1373, 1295, 1290, 1373, 1290, 1293, 1280, 1368, 1368,1294, 1293, 1368, 1372, 1292, 1290, 1291, 1371, 1375, 1280, 1372, 1281, 1293,1373, 1371, 1354, 1370, 1356, 1354, 1355, 1370, 1357, 1357, 1302, 1366, 1303,1368, 1354, 1355, 1356, 1303, 1366, 1371]
    tab1 = [1373, 1281, 1288, 1373, 1290, 1294, 1375, 1371,1289, 1281, 1280, 1293, 1289, 1280, 1373, 1294, 1289, 1280, 1372, 1288, 1375,1375, 1289, 1373, 1290, 1281, 1294, 1302, 1372, 1355, 1366, 1372, 1302, 1360, 1368, 1354, 1364, 1370, 1371, 1365, 1362, 1368, 1352, 1374, 1365, 1302]

    func = (lambda g, c, d: 
        (lambda x: (x.__setitem__('$', ''.join([(x['chr'] if ('chr' in x) else chr)((x['_'] if ('_' in x) else x)) for x['_'] in (x['s'] if ('s' in x) else s)[::-1]])), x)[-1])
        ((lambda x: (lambda f, x: f(f, x))
            ((lambda __, x: 
                ((lambda x: __(__, x))
                    ((lambda x: (x.__setitem__('i', ((x['i'] if ('i' in x) else i) + 1)), x)[-1])
                    ((lambda x: (x.__setitem__('s', ((x['s'] if ('s' in x) else s) + [((x['l'] if ('l' in x) else l)[(x['i'] if ('i' in x) else i)] ^ (x['c'] if ('c' in x) else c))])), x)[-1])
                    (x)
                )
                if (((x['g'] if ('g' in x) else g) % 4) and ((x['i'] if ('i' in x) else i)< (x['len'] if ('len' in x) else len)((x['l'] if ('l' in x) else l))))
                else x
            ), x)
        )
        ((lambda x: (x.__setitem__('!', []), x.__setitem__('s', x['!']), x)[-1])
        ((lambda x: (x.__setitem__('!', ((x['d'] if ('d' in x) else d) ^ (x['d'] if ('d' in x) else d))), x.__setitem__('i', x['!']), x)[-1])
        ((lambda x: (x.__setitem__('!', [(x['j'] if ('j' in x) else j) for x[ 'i'] in (x['zip'] if ('zip' in x) else zip)((x['l0'] if ('l0' in x) else l0), (x['l1'] if ('l1' in x) else l1)) for x['j'] in (x['i'] if ('i' in x) else i)]), x.__setitem__('l', x['!']), x)[-1])
        ((lambda x: (x.__setitem__('!', tab1), x.__setitem__('l1', x['!']), x)[-1])
        ((lambda x: (x.__setitem__('!', tab0), x.__setitem__('l0', x['!']), x)[-1])
        ({ '
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计