Post

Return to VDSO using ELF Auxiliary Vectors leck

http://v0ids3curity.blogspot.kr/2014/12/return-to-vdso-using-elf-auxiliary.html

2016/11/21 - [System/etc] - Memory Layout, Segment / Stack layout

Linux Stack Layout with Auxiliary Vectors

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
0x7fffffffe0e8: 0x00007ffff7a36f45    ( main's ret )
0x7fffffffe0f0: 0x0000000000000000    ( argc )
0x7fffffffe0f8: 0x00007fffffffe1c8    ( \*\*argv )

  
....................
0x7fffffffe1c0: 0x0000000000000000    ( argc )
0x7fffffffe1c8: 0x00007fffffffe5a7    ( argv[0] )
0x7fffffffe1d0: 0x0000000000000000    ( last argv[n] == NULL )
0x7fffffffe1d8: 0x00007fffffffe5d5    ( envp[0] )
0x7fffffffe1e0: 0x00007fffffffe67f    ( envp[1] )
..........envp[]..........
0x7fffffffe420: 0x0000000000000000    ( last envp[n] == NULL )
/\*\*\*\*\*     auxv[]     \*\*\*\*\*/
key                     value
0x7fffffffe428: 0x0000000000000021      0x00007ffff7ffa000      // AT\_SYSINFO\_EHDR
0x7fffffffe438: 0x0000000000000010      0x000000001f8bfbff
0x7fffffffe448: 0x0000000000000006      0x0000000000001000
0x7fffffffe458: 0x0000000000000011      0x0000000000000064
0x7fffffffe468: 0x0000000000000003      0x0000000000400040
0x7fffffffe478: 0x0000000000000004      0x0000000000000038
0x7fffffffe488: 0x0000000000000005      0x0000000000000009
0x7fffffffe498: 0x0000000000000007      0x00007ffff7dda000
0x7fffffffe4a8: 0x0000000000000008      0x0000000000000000
0x7fffffffe4b8: 0x0000000000000009      0x0000000000400490
0x7fffffffe4c8: 0x000000000000000b      0x00000000000003e8
0x7fffffffe4d8: 0x000000000000000c      0x00000000000003e8
0x7fffffffe4e8: 0x000000000000000d      0x00000000000003e8
0x7fffffffe4f8: 0x000000000000000e      0x00000000000003e8
0x7fffffffe508: 0x0000000000000017      0x0000000000000000
0x7fffffffe518: 0x0000000000000019      0x00007fffffffe559      // AT\_RANDOM
0x7fffffffe528: 0x000000000000001f      0x00007fffffffefca
0x7fffffffe538: 0x000000000000000f      0x00007fffffffe569
0x7fffffffe548: 0x0000000000000000      0x0000000000000000
0x7fffffffe558
....................
0x7fffffffe5a7: "filename"     ( argv strings )
..........
0x7fffffffe5d5: "ENV=value"    ( envp strings )
....................
program name (== argv[0] string )
NULL
....................

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ set env LD\_SHOW\_AUXV=1
$ r
AT\_SYSINFO\_EHDR: 0x7ffff7ffa000      // VDSO addr
AT\_HWCAP:        1f8bfbff
AT\_PAGESZ:       4096
AT\_CLKTCK:       100
AT\_PHDR:         0x400040
AT\_PHENT:        56
AT\_PHNUM:        9
AT\_BASE:         0x7ffff7dda000
AT\_FLAGS:        0x0
AT\_ENTRY:        0x400490            // Entry point of program
AT\_UID:          1000
AT\_EUID:         1000
AT\_GID:          1000
AT\_EGID:         1000
AT\_SECURE:       0                   // secure-execution mode
AT\_RANDOM:       0x7fffffffe559      // point to end of AUXV table
AT\_EXECFN:       /home/ubuntu/workspace/study/heap/fastbin\_dup
AT\_PLATFORM:     x86\_64

AT\_RANDOM을 이용해 stack offset을 계산하는 것은 remote라면 거의 불가능하다. 서버 측 쉘의 환경변수가 얼마나 있는지도 모르고, 각 stack frame size같은 것도 정확히 맞출 수 없기 때문에 어느 정도는 추측해야 한다.

sys_read-sys_write leak

#1 sys_read
1
2
rax : 0x0  /  rsi : (stack)  /  rdi : (stdin, 0)  /  rdx : size    ( x86\_64 )

* rdi에서 읽어서, rsi에 쓰게 된다. 결과적으로 read하는 곳이 stdin이라 rdi : stdin 위와 같은 상태에서 syscall 해서 sys\_read를 호출한 다음 rax=0x1로 만들기 위해 \n만 전송한다.

#2 sys_write

sys\_read가 끝난 이후 레지스터 상태는 다음과 같다.

1
2
rax : 0x1  /  rsi : (stack)  /  rdi : (stdin, 0)  /  rdx : size    ( x86\_64 )

* rsi에서 읽어서, rdi에 쓰게 된다. 결과적으로 write하는 곳이 stdin이라 rdi : stdin stdin이든 stdout이든 stderr이든 입출력에 아무거나 사용해도 되기 때문에 이대로 sys\_write를 호출하면 sys\_read에서 데이터를 저장해놓은 지점부터 size만큼 leak.

이를 AUXV leak에 사용할 수 있다.

Return to vDSO

gadget server 측에서 사용하는 vDSO page를 알고 있을 때 사용할 수 있기 때문에 제약이 꽤 크다. server OS를 알고 있다면 vDSO page도 알아낼 수는 있을 듯.

64bit에서는 parameter push를 위해 rdi, rsi, rdx를 조작할 수 있어야 하는데, Ubuntu 14.04.5 LTS에는 다음을 제외하면 적당한 gadget이 없다.

1
2
3
4
5
0x7ffff7ffa9d6:      syscall
0x7ffff7ffa9d8:      pop    rbx
0x7ffff7ffa9d9:      pop    rbp
0x7ffff7ffa9da:      ret

아무튼, vDSO를 사용할 수 있는 환경이라면 이런 유용한 gadget들을 찾아보는게 도움이 된다.

vDSO addr

  1. fork()하는 경우 vDSO addr이 매번 같기 때문에 AT\_SYSINFO\_EHDR leak 하면 바로 알아낼 수 있다.
  2. vDSO page는 random하게 결정되지만, randomize range가 그리 크지 않아 brute force가 불가능한 수준은 아닌 듯.

https://github.com/umbum/pwn/blob/master/vdso/entropy.py

This post is licensed under CC BY 4.0 by the author.