用手机打 CTF 是种什么体验

刚刚阅读1回复0
kanwenda
kanwenda
  • 管理员
  • 注册排名1
  • 经验值155115
  • 级别管理员
  • 主题31023
  • 回复0
楼主

用手机打CTF的选手:

用 iPhone 打 CTF (娱乐向)302 播放 · 2 附和视频​ 布景

比来 iSH 在 Apple Store 上架了,之前不断抢不到 testflight 的配额,罕见强管控的苹果会让那种 Terminal 类的应用发布,所以第一时间下载来玩玩。测试之后发现能够当做是一个简单的 Linux 虚拟机,至少常用的号令都没什么问题,正都雅到看雪论坛有个 CTF,于是就试一下,能不克不及用手机来打一局 CTF :)

情况

iSH 是一个开源的 Terminal Emulator,在 iOS 中运行,利用用户态的 x86 指令模仿以及 syscall 翻译实现。iSH 中利用的是 Alphine Linux 镜像,用过 Docker 的应该都不会目生。Alphine 是个十分轻量级的 Linux 发行版,在官网上能够间接下载各个平台预编译的镜像,日常平凡有测试内核需求又不满足于 ramfs 的能够利用下载的镜像停止便利测试。

在 iSH 中已经有了一个简单的 Alphine 情况,可能是是因为苹果商铺审核的原因,在此中并没有预置保证理东西。我们能够下载一个完好的 iso 从头挂载,但是有更简单的办法,间接从 Alphine Packages 中下载静态编译版本的 apk-tools-static:

wget http://dl-cdn.alpinelinux.org/alpine/latest-stable/main/x86/apk-tools-static-2.10.5-r1.apk tar -zxvf apk-tools-static-2.10.5-r1.apk ./sbin/apk.static -X https://mirrors.tuna.tsinghua.edu.cn --initdb add apk-tools apk update rm -rf ./sbin

我们用下载的 apk-tools-static 来安拆 apk-tools,然后保证理东西 apk 就能够一般利用了。若是初始化的时候没有指定镜像源,能够利用下面的号令修改为清华大学的镜像源,加快国内的拜候速度:

sed -i s/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g /etc/apk/repositories

之后需要的东西就能够自行安拆了,我那里只需要 radare2 和 Python:

apk add radare2 apk add python3

标题问题是看雪 CTF2020 的签到题: https://ctf.pediy.com/game-season_fight-158.htm

Write Up

解题过程在文章开头的视频中已经能够看到了,那里仍是用文字介绍下解题思绪。

起首 rabin2 查看目的法式的信息:

$ rabin2 -I kctf2020.exe arch x86 baddr 0x140000000 binsz 12288 bintype pe bits 64 canary false retguard false class PE32+ cmp.csum 0x0000bcb6 compiled Thu Nov 12 03:49:32 2020 crypto false dbg_file D:\Users\admin\Documents\Visual Studio 2015\Projects\ctf2020\ConsoleApplication1\x64\Release\ConsoleApplication1.pdb endian little havecode true hdr.csum 0x00000000 guid 4EF50FB8F5E74CC481D01589FC111B1A1 laddr 0x0 lang c linenum false lsyms false machine AMD 64 maxopsz 16 minopsz 1 nx true os windows overlay false cc ms pcalign 0 pic true relocs false signed false sanitiz false static false stripped false subsys Windows CUI va true

能够看到是 PE64 的的法式,鉴于是签到题,所以就间接逆向阐发了:

$ r2 kctf2020.exe -- [0x14000154c]> s main [0x140001000]> af [0x140001000]> pd 20 ;-- section..text: ┌ 459: int main (int argc, char **argv, char **envp); │ bp: 7 (vars 7, args 0) │ sp: 14 (vars 14, args 0) │ rg: 0 (vars 0, args 0) │ 0x140001000 4055 push rbp ; [00] -r-x section size 8192 named .text │ 0x140001002 488dac24c0fe. lea rbp, [rsp - 0x140] │ 0x14000100a 4881ec400200. sub rsp, 0x240 │ 0x140001011 488b05e82f00. mov rax, qword [section..data] ; [0x140004000:8]=0x2b992ddfa232 ; "2\xa2\xdf-\x99+" │ 0x140001018 4833c4 xor rax, rsp │ 0x14000101b 488985300100. mov qword [var_130h], rax │ 0x140001022 33d2 xor edx, edx │ 0x140001024 488d4c2430 lea rcx, [var_30h] │ 0x140001029 41b800010000 mov r8d, 0x100 ; 256 │ 0x14000102f e8ce0e0000 call 0x140001f02 │ 0x140001034 33d2 xor edx, edx │ 0x140001036 488d4d30 lea rcx, [var_bp_30h] │ 0x14000103a 41b800010000 mov r8d, 0x100 ; 256 │ 0x140001040 e8bd0e0000 call 0x140001f02 │ 0x140001045 488d0dd42100. lea rcx, str.KCTF_2020 ; 0x140003220 ; "KCTF 2020!\n" │ 0x14000104c e8ff010000 call 0x140001250 │ 0x140001051 488d0dd82100. lea rcx, str.http:__bbs.pediy.com ; 0x140003230 ; "http://bbs.pediy.com\n" │ 0x140001058 e8f3010000 call 0x140001250 │ 0x14000105d 488d0de42100. lea rcx, str.Please_input_your_flag: ; 0x140003248 ; "Please input your flag: " │ 0x140001064 e8e7010000 call 0x140001250

在 0x140001250 地址处经常有挪用,按照参数能够推测是打印函数,验证一下:

[0x140001000]> s 0x140001250 [0x140001250]> af [0x140001250]> pdf ; CALL XREFS from main @ 0x14000104c, 0x140001058, 0x140001064, 0x14000119b, 0x1400011ab ┌ 85: int printf (const char *format); │ ; var int64_t var_20h_2 @ rsp+0x20 │ ; var int64_t var_8h @ rsp+0x50 │ ; var int64_t var_10h @ rsp+0x58 │ ; var int64_t var_18h @ rsp+0x60 │ ; var int64_t var_20h @ rsp+0x68 │ ; arg int64_t arg1 @ rcx │ ; arg int64_t arg2 @ rdx │ ; arg int64_t arg3 @ r8 │ ; arg int64_t arg4 @ r9 │ 0x140001250 48894c2408 mov qword [var_8h], rcx ; arg1 │ 0x140001255 4889542410 mov qword [var_10h], rdx ; arg2 │ 0x14000125a 4c89442418 mov qword [var_18h], r8 ; arg3 │ 0x14000125f 4c894c2420 mov qword [var_20h], r9 ; arg4 │ 0x140001264 53 push rbx │ 0x140001265 56 push rsi │ 0x140001266 57 push rdi │ 0x140001267 4883ec30 sub rsp, 0x30 │ 0x14000126b 488bf9 mov rdi, rcx │ 0x14000126e 488d742458 lea rsi, [var_10h] │ 0x140001273 b901000000 mov ecx, 1 │ 0x140001278 ff150a1f0000 call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll___acrt_iob_func] ; [0x140003188:8]=0x3a98 reloc.api_ms_win_crt_stdio_l1_1_0.dll___acrt_iob_func │ 0x14000127e 488bd8 mov rbx, rax │ 0x140001281 e8baffffff call fcn.140001240 │ 0x140001286 4533c9 xor r9d, r9d │ 0x140001289 4889742420 mov qword [var_20h_2], rsi │ 0x14000128e 4c8bc7 mov r8, rdi │ 0x140001291 488bd3 mov rdx, rbx │ 0x140001294 488b08 mov rcx, qword [rax] │ 0x140001297 ff15e31e0000 call qword [sym.imp.api_ms_win_crt_stdio_l1_1_0.dll___stdio_common_vfprintf] ; [0x140003180:8]=0x3aaa reloc.api_ms_win_crt_stdio_l1_1_0.dll___stdio_common_vfprintf │ 0x14000129d 4883c430 add rsp, 0x30 │ 0x1400012a1 5f pop rdi │ 0x1400012a2 5e pop rsi │ 0x1400012a3 5b pop rbx └ 0x1400012a4 c3 ret

公然是打印函数,间接重定名一下,清新一些:

[0x140001250]> af printf [0x140001250]> s- [0x140001000]> pd 20 ;-- section..text: ┌ 459: int main (int argc, char **argv, char **envp); │ bp: 7 (vars 7, args 0) │ sp: 14 (vars 14, args 0) │ rg: 0 (vars 0, args 0) │ 0x140001000 4055 push rbp ; [00] -r-x section size 8192 named .text │ 0x140001002 488dac24c0fe. lea rbp, [rsp - 0x140] │ 0x14000100a 4881ec400200. sub rsp, 0x240 │ 0x140001011 488b05e82f00. mov rax, qword [section..data] ; [0x140004000:8]=0x2b992ddfa232 ; "2\xa2\xdf-\x99+" │ 0x140001018 4833c4 xor rax, rsp │ 0x14000101b 488985300100. mov qword [var_130h], rax │ 0x140001022 33d2 xor edx, edx │ 0x140001024 488d4c2430 lea rcx, [var_30h] │ 0x140001029 41b800010000 mov r8d, 0x100 ; 256 │ 0x14000102f e8ce0e0000 call 0x140001f02 │ 0x140001034 33d2 xor edx, edx │ 0x140001036 488d4d30 lea rcx, [var_bp_30h] │ 0x14000103a 41b800010000 mov r8d, 0x100 ; 256 │ 0x140001040 e8bd0e0000 call 0x140001f02 │ 0x140001045 488d0dd42100. lea rcx, str.KCTF_2020 ; 0x140003220 ; "KCTF 2020!\n" │ 0x14000104c e8ff010000 call printf ; int printf(const char *format) │ 0x140001051 488d0dd82100. lea rcx, str.http:__bbs.pediy.com ; 0x140003230 ; "http://bbs.pediy.com\n" │ 0x140001058 e8f3010000 call printf ; int printf(const char *format) │ 0x14000105d 488d0de42100. lea rcx, str.Please_input_your_flag: ; 0x140003248 ; "Please input your flag: " │ 0x140001064 e8e7010000 call printf ; int printf(const char *format)

同理,把 scanf 也重定名上,先看法式开头的逻辑:

│ 0x140001069 41b800010000 mov r8d, 0x100 ; 256 │ 0x14000106f 488d542430 lea rdx, [var_30h] │ 0x140001074 488d0de92100. lea rcx, [0x140003264] ; "%s" │ 0x14000107b e860010000 call scanf ; int scanf(const char *format) │ 0x140001080 488d542430 lea rdx, [var_30h] │ 0x140001085 4883c9ff or rcx, 0xffffffffffffffff │ 0x140001089 0f1f80000000. nop dword [rax] │ ┌─> 0x140001090 48ffc1 inc rcx │ ╎ 0x140001093 803c0a00 cmp byte [rdx + rcx], 0 │ └─< 0x140001097 75f7 jne 0x140001090 │ 0x140001099 83f90c cmp ecx, 0xc ; 12 │ ┌─< 0x14000109c 0f8502010000 jne 0x1400011a4 │ │ 0x1400010a2 807c243066 cmp byte [var_30h], 0x66 │ ┌──< 0x1400010a7 0f85f7000000 jne 0x1400011a4 │ ││ 0x1400010ad 807c24316c cmp byte [var_31h], 0x6c │ ┌───< 0x1400010b2 0f85ec000000 jne 0x1400011a4 │ │││ 0x1400010b8 807c243261 cmp byte [var_32h], 0x61 │ ┌────< 0x1400010bd 0f85e1000000 jne 0x1400011a4 │ ││││ 0x1400010c3 807c243367 cmp byte [var_33h], 0x67 │ ┌─────< 0x1400010c8 0f85d6000000 jne 0x1400011a4 │ │││││ 0x1400010ce 807c24347b cmp byte [var_34h], 0x7b │ ┌──────< 0x1400010d3 0f85cb000000 jne 0x1400011a4 │ ││││││ 0x1400010d9 807c243b7d cmp byte [var_3bh], 0x7d │ ┌───────< 0x1400010de 0f85c0000000 jne 0x1400011a4

var_30h 是输入的字符串,别离停止字节和长度比对,要求输入字符串长度为 12,而且格局为 flag{xxxxxx},那里有个技巧是 radare2 中 VV 形式下的跳转:

tab/shift+tab: 跳转到下一个/前一个 basic blockt/f: 跳转到 true/false 分收u/U: undo/redo 跳转

然后是 flag 中间字符的判断:

0x1400010e4 440fb74c2439 movzx r9d, word [var_39h] 0x1400010ea 4c8d442420 lea r8, [var_20h] 0x1400010ef 448b542435 mov r10d, dword [var_35h] 0x1400010f4 33c0 xor eax, eax 0x1400010f6 6644894c2424 mov word [var_24h], r9w 0x1400010fc 8bd0 mov edx, eax 0x1400010fe 4489542420 mov dword [var_20h], r10d 0x140001103 0f1f4000 nop dword [rax] 0x140001107 660f1f840000. nop word [rax + rax]

那里利用 mov dword 来停止拷贝,将 var_35h 拷贝 到 var_20h 中,同时 r8 存放器指向 var_20h。接着先轮回逐个判断字符c - 0x30能否小于 9,若是有大于 9 的就报错:

┌─> 0x140001110 410fb608 movzx ecx, byte [r8] ╎ 0x140001114 80e930 sub cl, 0x30 ; 48 ╎ 0x140001117 80f909 cmp cl, 9 ; 9 ┌──< 0x14000111a 0f8784000000 ja 0x1400011a4 │╎ 0x140001120 ffc2 inc edx │╎ 0x140001122 49ffc0 inc r8 │╎ 0x140001125 83fa06 cmp edx, 6 ; 6 │└─< 0x140001128 72e6 jb 0x140001110 │ 0x14000112a ...

接着颠末下面的判断,记得 var_30h 是我们的输入,长度为 12,格局为 flag{xxxxxx}:

0x14000112a 0fb6542437 movzx edx, byte [var_37h] 0x14000112f 4c8d053e2100. lea r8, [0x140003274] ; "2;=EFI" 0x140001136 0fb64c2436 movzx ecx, byte [var_36h] 0x14000113b 80ea30 sub dl, 0x30 ; 48 0x14000113e 80e930 sub cl, 0x30 ; 48 0x140001141 44885530 mov byte [var_bp_30h], r10b 0x140001145 4102ca add cl, r10b 0x140001148 4180e930 sub r9b, 0x30 ; 48 0x14000114c 02d1 add dl, cl 0x14000114e 884d31 mov byte [var_bp_31h], cl 0x140001151 0fb64c2438 movzx ecx, byte [var_38h] 0x140001156 80e930 sub cl, 0x30 ; 48 0x140001159 885532 mov byte [var_bp_32h], dl 0x14000115c 02ca add cl, dl 0x14000115e 488d5530 lea rdx, [var_bp_30h] 0x140001162 4402c9 add r9b, cl 0x140001165 884d33 mov byte [var_bp_33h], cl 0x140001168 0fb64c243a movzx ecx, byte [var_3ah] 0x14000116d 80e930 sub cl, 0x30 ; 48 0x140001170 44884d34 mov byte [var_bp_34h], r9b 0x140001174 4102c9 add cl, r9b 0x140001177 884d35 mov byte [var_bp_35h], cl 0x14000117a 660f1f440000 nop word [rax + rax] ┌─> 0x140001180 0fb60c02 movzx ecx, byte [rdx + rax] ╎ 0x140001184 48ffc0 inc rax ╎ 0x140001187 413a4c00ff cmp cl, byte [r8 + rax - 1] ┌──< 0x14000118c 7516 jne 0x1400011a4 │╎ 0x14000118e 4883f807 cmp rax, 7 ; 7 │└─< 0x140001192 75ec jne 0x140001180 │ 0x140001194 488d0de52000. lea rcx, [0x140003280] ; "You are winner!\n" │ 0x14000119b e8b0000000 call printf ; int printf(const char *format)

设flag{}中间的内容为flag,那里的逻辑能够写成伪代码:

buf[0] = flag[0] buf[1] = buf[0] + flag[1] - 48 buf[2] = buf[1] + flag[2] - 48 buf[3] = buf[2] + flag[3] - 48 buf[4] = buf[3] + flag[4] - 48 buf[5] = buf[4] + flag[5] - 48 if buf == "2;=EFI": printf("You are winner!\n")

反向计算,可得:

flag[0] = buf[0] flag[1] = buf[1] - buf[0] + 48 flag[2] = buf[2] - buf[1] + 48 flag[3] = buf[3] - buf[2] + 48 flag[4] = buf[4] - buf[3] + 48 flag[5] = buf[5] - buf[4] + 48

所以计算的 flag 为:

$ ./solve.py flag: b292813

最末谜底应该是 flag{292813},若是有 windows 情况的话能够停止动态验证,在 Linux/MacOS 情况中能够用 wine 来停止验证,如下:

$ wine64 kctf2020.exe 0025:err:plugplay:runloop_thread Couldnt open IOHIDManager. 0009:fixme:vcruntime:__telemetry_main_invoke_trigger (0x0) KCTF 2020! http://bbs.pediy.com Please input your flag: 0009:fixme:msvcrt:MSVCRT__stdio_common_vfscanf options 3 not handled flag{292813} You are winner! 0009:fixme:vcruntime:__telemetry_main_return_trigger (0x0) 跋文

安拆了 iSH 之后,我的手机时间分配酿成了如许:

手机上的 Terminal 能够用来做什么呢?那里能够列举一些:

用来 ssh 登录到办事器停止办理用来随时查看 man page用来编写和运行主动化脚本停止一些简单的号令行操做...

此中 ssh 对我来说是个刚需,因为在此之前苹果上不断没有一个好用且免费的 ssh client,只能用 Termius 勉强过活。不外可惜的是因为苹果的战略,iSH 暂时还不克不及在后台运行,否则间接 ssh -R 还能当做隧道用,岂不美哉?

当然,iSH 仍是有良多局限性的,好比只能模仿 x86 的指令集,并且因为是软件模仿,因而运行效率比力低。若是要施行复杂的使命,好比 gcc 编译大型项目,仍是更好在 PC 上运行了。

0
回帖 返回旅游

用手机打 CTF 是种什么体验 期待您的回复!

取消
载入表情清单……
载入颜色清单……
插入网络图片

取消确定

图片上传中
编辑器信息
提示信息