Bytectf 2019 s390 Writeup

Bytectf 2019 s390 Writeup

Category: RE

Score: 833

Solved: 5

在上一章中已经讲到将IBM ZOS S390搭建好了,在hercules上安装了Ubuntu 18.04 s390x。

在本文中将讲解如何对其进行静态分析和动态调试,以及IBM z Architecture的汇编资料。如果你有更好的想法,欢迎与我交流。

由于目前的主流反编译器、反汇编工具如IDA、Ghidra、radare2、objdump均无法有效的跨平台反汇编,更没有办法调试,因此我选择搭建s390环境,并在该环境中分析这个elf文件。

搭好了环境,先运行一下:

image-20190909233218225

动态调试

直接用s390上使用apt-get install gdb,但该版本的gdb有点问题,虽然能够调试,但无法使用step、next命令,只能通过b、c来调试。可能的解决方案:下载gdb源码,在s390上编译。(在虚拟机中的虚拟机中编译,很慢很慢…)

image-20190909233452823

image-20190909233523003

静态分析

查看所有段

1
$ objdump -h ./s390

image-20190909233629848

使用objdump反汇编,并导出到文件

1
$ objdump -s ./s390 > dis.txt

使用objdump把rodata段dump出来,并在编辑器中打开,

1
$ objdump -j .rodata -s ./s390 > rodata.txt

image-20190909145824879

定位到几个关键字符串地址,在反汇编代码中查找,顺利定位到main函数:

image-20190909150037182

对照http://www.tachyonsoft.com/inst390m.htm了解汇编指令,并标注其含义。

同时进行动态调试,发现循环4次scanf(“%llu”, &(%r11+168)+8*(%r11+164)),输入4个数字(%r11可视为栈指针)

main函数(0x80000920):

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
80000920:	eb bf f0 58 00 24 	stmg	%r11,%r15,88(%r15)                              ; main
80000926: e3 f0 ff 50 ff 71 lay %r15,-176(%r15)
8000092c: b9 04 00 bf lgr %r11,%r15
80000930: a7 29 00 20 lghi %r2,32
80000934: c0 e5 00 00 9d 62 brasl %r14,0x800143f8
8000093a: b9 04 00 12 lgr %r1,%r2
8000093e: e3 10 b0 a8 00 24 stg %r1,168(%r11)
80000944: e5 4c b0 a4 00 00 mvhi 164(%r11),0
8000094a: c0 20 00 03 68 93 larl %r2,0x8006da70
80000950: c0 e5 00 00 3e 74 brasl %r14,0x80008638
80000956: c0 20 00 03 68 8e larl %r2,0x8006da72
8000095c: c0 e5 00 00 3e 6e brasl %r14,0x80008638
80000962: c0 20 00 03 68 9a larl %r2,0x8006da96
80000968: c0 e5 00 00 3e 68 brasl %r14,0x80008638
8000096e: c0 20 00 03 68 a7 larl %r2,0x8006dabc
80000974: c0 e5 00 00 3e 62 brasl %r14,0x80008638
8000097a: c0 20 00 03 68 b4 larl %r2,0x8006dae2
80000980: c0 e5 00 00 3e 5c brasl %r14,0x80008638
80000986: c0 20 00 03 68 c1 larl %r2,0x8006db08
8000098c: c0 e5 00 00 3e 56 brasl %r14,0x80008638
80000992: c0 20 00 03 68 ce larl %r2,0x8006db2e
80000998: c0 e5 00 00 3e 50 brasl %r14,0x80008638
8000099e: c0 20 00 03 68 db larl %r2,0x8006db54
800009a4: c0 e5 00 00 3e 4a brasl %r14,0x80008638
800009aa: c0 20 00 03 68 e8 larl %r2,0x8006db7a
800009b0: c0 e5 00 00 3e 44 brasl %r14,0x80008638
800009b6: c0 20 00 03 68 5d larl %r2,0x8006da70
800009bc: c0 e5 00 00 3e 3e brasl %r14,0x80008638
800009c2: c0 20 00 03 68 57 larl %r2,0x8006da70
800009c8: c0 e5 00 00 3e 38 brasl %r14,0x80008638
800009ce: c0 20 00 03 68 e9 larl %r2,0x8006dba0 ; input your answer
800009d4: c0 e5 00 00 3e 32 brasl %r14,0x80008638
800009da: e5 4c b0 a4 00 00 mvhi 164(%r11),0
800009e0: a7 f4 00 16 j 0x80000a0c
800009e4: e3 10 b0 a4 00 14 lgf %r1,164(%r11) ; Load
800009ea: eb 11 00 03 00 0d sllg %r1,%r1,3 ; Shift Left Single Logical
800009f0: e3 10 b0 a8 00 08 ag %r1,168(%r11) ; Add
800009f6: b9 04 00 31 lgr %r3,%r1 ; Load %r3->%r1 0x8009e420
800009fa: c0 20 00 03 68 dc larl %r2,0x8006dbb2 ; %llu
80000a00: c0 e5 00 00 3a 3c brasl %r14,0x80007e78 ; scanf
80000a06: eb 01 b0 a4 00 6a asi 164(%r11),1
80000a0c: 58 10 b0 a4 l %r1,164(%r11)
80000a10: a7 1e 00 03 chi %r1,3
80000a14: a7 c4 ff e8 jle 0x800009e4
80000a18: e3 20 b0 a8 00 04 lg %r2,168(%r11) ; 在%r2的地址中
80000a1e: c0 e5 ff ff fe a1 brasl %r14,0x80000760 ; check and printf
80000a24: a7 18 00 00 lhi %r1,0
80000a28: b9 14 00 11 lgfr %r1,%r1
80000a2c: b9 04 00 21 lgr %r2,%r1
80000a30: e3 40 b1 20 00 04 lg %r4,288(%r11)
80000a36: eb bf b1 08 00 04 lmg %r11,%r15,264(%r11)
80000a3c: 07 f4 br %r4

接下来call 0x80000760,并在该函数中检查输入并输出结果。(Architecture z的汇编与MIPS类似,brasl为跳转并链接,即为call)

最主要的是0x80000760函数,不过现在我们有汇编代码、有参考手册、有gdb动态调试,分析其功能只是时间问题。以下是我对0x80000760函数的分析:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    0x80000760:	eb bf f0 58 00 24 	stmg	%r11,%r15,88(%r15)                            ; Store Multiple ; check and printf
0x80000766: e3 f0 ff 28 ff 71 lay %r15,-216(%r15) ; Load Address
0x8000076c: b9 04 00 bf lgr %r11,%r15 ; Load
0x80000770: e3 20 b0 a0 00 24 stg %r2,160(%r11) ; Store %r2 -> %r11+160
0x80000776: e5 4c b0 a8 00 00 mvhi 168(%r11),0 ; Move Immediate
0x8000077c: e3 10 b0 a0 00 04 lg %r1,160(%r11) ; $r11 + 160; $r11 + 192为输入
0x80000782: e3 10 b0 c0 00 24 stg %r1,192(%r11)
0x80000788: e3 10 b0 c0 00 04 lg %r1,192(%r11)
0x8000078e: e3 20 10 00 00 04 lg %r2,0(%r1)
0x80000794: e3 10 b0 c0 00 04 lg %r1,192(%r11)
0x8000079a: a7 1b 00 08 aghi %r1,8 ; Add Halfword Immediate
0x8000079e: e3 10 10 00 00 04 lg %r1,0(%r1)
0x800007a4: b9 82 00 12 xgr %r1,%r2
0x800007a8: e3 10 b0 c8 00 24 stg %r1,200(%r11)
0x800007ae: e3 10 b0 c0 00 04 lg %r1,192(%r11)
0x800007b4: a7 1b 00 10 aghi %r1,16
0x800007b8: e3 20 10 00 00 04 lg %r2,0(%r1)
0x800007be: e3 10 b0 c0 00 04 lg %r1,192(%r11)
0x800007c4: a7 1b 00 18 aghi %r1,24
0x800007c8: e3 10 10 00 00 04 lg %r1,0(%r1)
0x800007ce: b9 82 00 12 xgr %r1,%r2
0x800007d2: e3 10 b0 d0 00 24 stg %r1,208(%r11)
0x800007d8: e5 4c b0 ac 00 00 mvhi 172(%r11),0
0x800007de: e5 4c b0 a8 00 00 mvhi 168(%r11),0 ; 初始化结束
0x800007e4: a7 f4 00 73 j 0x800008ca J2
LA1 0x800007e8: e5 48 b0 b8 00 01 mvghi 184(%r11),1 ; Move Immediate
0x800007ee: e5 48 b0 b0 25 3d mvghi 176(%r11),9533 ; [176] <- 9533
0x800007f4: a7 f4 00 4d j 0x8000088e J1
LA2 0x800007f8: e3 10 b0 b0 00 04 lg %r1,176(%r11)
0x800007fe: ec 11 3f bf 00 55 risbg %r1,%r1,63,191,0 ; Rotate then Insert Selected Bits
0x80000804: b9 02 00 11 ltgr %r1,%r1
0x80000808: a7 84 00 14 je 0x80000830 LA3
0x8000080c: e3 10 b0 a8 00 14 lgf %r1,168(%r11)
0x80000812: eb 11 00 03 00 0d sllg %r1,%r1,3 ; Shift Left Single Logical *8
0x80000818: e3 10 b0 c0 00 08 ag %r1,192(%r11) ; 取第[168*8]个输入
0x8000081e: e3 10 10 00 00 04 lg %r1,0(%r1)
0x80000824: e3 10 b0 b8 00 0c msg %r1,184(%r11) ; Multiply Single
0x8000082a: e3 10 b0 b8 00 24 stg %r1,184(%r11) ; 乘完放回[184]
LA3 0x80000830: e3 10 b0 a8 00 14 lgf %r1,168(%r11)--|
0x80000836: eb 11 00 03 00 0d sllg %r1,%r1,3 | -------------------------------- ; 取第i个输入的数的地址
0x8000083c: e3 10 b0 c0 00 08 ag %r1,192(%r11)--|
0x80000842: e3 20 b0 a8 00 14 lgf %r2,168(%r11)--|
0x80000848: eb 22 00 03 00 0d sllg %r2,%r2,3 |---------------------------------- ; 第i个输入的数到 %r3
0x8000084e: e3 20 b0 c0 00 08 ag %r2,192(%r11) |
0x80000854: e3 30 20 00 00 04 lg %r3,0(%r2)-----|
0x8000085a: e3 20 b0 a8 00 14 lgf %r2,168(%r11) |
0x80000860: eb 22 00 03 00 0d sllg %r2,%r2,3 |---------------------------------- ; 再取一次到 %r2
0x80000866: e3 20 b0 c0 00 08 ag %r2,192(%r11) |
0x8000086c: e3 20 20 00 00 04 lg %r2,0(%r2)-----|
0x80000872: b9 0c 00 23 msgr %r2,%r3
0x80000876: e3 20 10 00 00 24 stg %r2,0(%r1) ; %r2 %r3 相乘放回
0x8000087c: e3 10 b0 b0 00 04 lg %r1,176(%r11)
0x80000882: eb 11 00 01 00 0c srlg %r1,%r1,1
0x80000888: e3 10 b0 b0 00 24 stg %r1,176(%r11)
J1 0x8000088e: e3 10 b0 b0 00 04 lg %r1,176(%r11) ; 每次[176] >> 1 while([176] != 0)
0x80000894: b9 02 00 11 ltgr %r1,%r1 ; Load and Test
0x80000898: a7 74 ff b0 jne 0x800007f8 LA2 ;
0x8000089c: c0 10 00 03 68 c2 larl %r1,0x8006da20 ; check data
0x800008a2: e3 20 b0 a8 00 14 lgf %r2,168(%r11)
0x800008a8: eb 22 00 03 00 0d sllg %r2,%r2,3
0x800008ae: e3 12 10 00 00 04 lg %r1,0(%r2,%r1)
0x800008b4: e3 10 b0 b8 00 20 cg %r1,184(%r11) ; 比较 %r1: check data %r11+184: transformed input
0x800008ba: a7 74 00 05 jne 0x800008c4
0x800008be: eb 01 b0 ac 00 6a asi 172(%r11),1 ; 正确 172 + 1
0x800008c4: eb 01 b0 a8 00 6a asi 168(%r11),1 ; 循环计数 168
J2 0x800008ca: 58 10 b0 a8 l %r1,168(%r11)
0x800008ce: a7 1e 00 03 chi %r1,3 ; 168 小于等于 3 循环
0x800008d2: a7 c4 ff 8b jle 0x800007e8 LA1
0x800008d6: 58 10 b0 ac l %r1,172(%r11) ; 比较 172是否等于4
0x800008da: a7 1e 00 04 chi %r1,4
0x800008de: a7 74 00 10 jne 0x800008fe ; go to wrong
0x800008e2: e3 40 b0 d0 00 04 lg %r4,208(%r11) ; %llx
0x800008e8: e3 30 b0 c8 00 04 lg %r3,200(%r11) ; $llx
0x800008ee: c0 20 00 03 68 a9 larl %r2,0x8006da40 ; Congratulation
0x800008f4: c0 e5 00 00 3a 66 brasl %r14,0x80007dc0 ; printf
0x800008fa: a7 f4 00 09 j 0x8000090c
0x800008fe: c0 20 00 03 68 b4 larl %r2,0x8006da66 ; wrong
0x80000904: c0 e5 00 00 3e 9a brasl %r14,0x80008638
0x8000090a: 18 00 lr %r0,%r0
0x8000090c: e3 40 b1 48 00 04 lg %r4,328(%r11)
0x80000912: eb bf b1 30 00 04 lmg %r11,%r15,304(%r11)
0x80000918: 07 f4 br %r4 ; return

有一个五操作数指令risbg %r1,%r1,63,191,0手册上解释其功能为Rotate then Insert Selected Bits,不太理解,不过根据动态调试的结果,其功能为取最低位放回%r1,等价与%r1 = %r1 & 1

分析check函数0x80000760可知其为一个快速幂算法,对四个输入进行快速幂运算,得到9533次方,与rodata中的密文进行比较,输出结果。

解密脚本

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
26
target =[
0x17e8ba3b95cb36e9,
0x5eb83e8108340e27,
0x298bb3acedff4e49,
0xed57a654e66e84c5,
]

import gmpy2
e = 9533
fi_n = 2**63
print gmpy2.invert(e, fi_n)
# 3149278923734637077

key = 3149278923734637077

for i in range(4):
print pow(target[i], key, 1 << 64)

"""
9997764242830658953
13181961895445880007
10486649486922394729
13099406429414088373

bytectf{3c508d1b7ecf514e2442609ba1bb72dc}
"""

其他

可以在ubuntu上安装gcc-s390x-linux-gnu,交叉编译静态链接有符号的二进制文件,帮助我们识别无符号elf中的libc函数,如scanf。

参考https://packages.ubuntu.com/xenial/gcc-s390x-linux-gnu,可使用`apt-get install gcc-5-s390x-linux-gnu`安装交叉编译器。

导出来的txt:

参考资料

z Architecture汇编手册:http://www.tachyonsoft.com/inst390m.htm

zpwn200题解:https://blog.dragonsector.pl/2014/02/olympic-ctf-2014-zpwn-200.html

zbin500题解:https://blog.dragonsector.pl/2014/02/olympic-ctf-2014-zbin-re-500.html