_environ troubleshooting

Python
iofile_stdout = libc_e.symbols[b'_IO_2_1_stdout_']
__environ = libc_e.symbols[b'__environ']

print(iofile_stdout - __environ)


root@a0b2cea1050c:/home/environ/environ# python3 exploit.py
-27264

이것의 결과는 libc 상의 Offset 을 계산한 것이다. 결과가 -27264

Python
pwndbg> p &__environ 
$1 = (char ***) 0x7ffff7ffe2d0 <environ>
pwndbg> p stdout    
$2 = (FILE *) 0x7ffff7fa9780 <_IO_2_1_stdout_>
pwndbg> p &_IO_2_1_stdout_
$3 = (struct _IO_FILE_plus *) 0x7ffff7fa9780 <_IO_2_1_stdout_>
pwndbg> p 0x7ffff7ffe2d0 - 0x7ffff7fa9780
$4 = 346960

그런데 gdb 상에서 계산을 해보면 346960 라는 완전히 다른 결과가 나온다. 뭐가 문제일까?

Python
pwndbg> vmmap 0x7ffff7ffe2d0 <environ>
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x7ffff7ffb000     0x7ffff7ffd000 r--p     2000  37000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
►   0x7ffff7ffd000     0x7ffff7fff000 rw-p     2000  39000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 +0x12d0
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
pwndbg> vmmap 0x7ffff7fa9780 <_IO_2_1_stdout_>
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x7ffff7fa4000     0x7ffff7fa8000 r--p     4000 215000 /usr/lib/x86_64-linux-gnu/libc.so.6
►   0x7ffff7fa8000     0x7ffff7faa000 rw-p     2000 219000 /usr/lib/x86_64-linux-gnu/libc.so.6 +0x1780

그러던 중 이상한 점을 발견했다. environ의 위치가

/usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 으로 되어 있는것

Python
root@a0b2cea1050c:/home/environ/environ# objdump -tT libc.so.6  | grep environ   
0000000000221200  w   DO .bss	0000000000000008  GLIBC_2.2.5 _environ
0000000000221200 g    DO .bss	0000000000000008  GLIBC_2.2.5 __environ
0000000000221200  w   DO .bss	0000000000000008  GLIBC_2.2.5 environ

bss 섹션에 있어야 할 얘가 완전하 이상한 곳에 위치해 있다.

Python

pwndbg> x/gx 0x7ffff7fa9780 + 27264
0x7ffff7fb0200 <environ>:	0x00007fffffffe698
pwndbg> x/gx 0x00007fffffffe698
0x7fffffffe698:	0x00007fffffffe8c3
pwndbg> p &_environ
$2 = (char ***) 0x7ffff7ffe2d0 <environ>
pwndbg> p 0x7ffff7ffe2d0
$3 = 140737354130128
pwndbg> x/gx 0x7ffff7ffe2d0
0x7ffff7ffe2d0 <environ>:	0x00007fffffffe698
pwndbg> 

따라서 내가 임의로 27264 값을 더한 위치를 찾아보니

엥? 0x7ffff7fb0200 <environ>: 0x00007fffffffe698 해당 위치에도 역시 같은 값이 존재한다.

Python
root@a0b2cea1050c:/home/environ/environ# objdump -tT /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 | grep environ
0000000000000000 l    df *ABS*	0000000000000000 dl-environ.c
0000000000000000 l    df *ABS*	0000000000000000 environ.c
000000000003b2d0 l     O .bss	0000000000000008 _environ
000000000003b2d0 l     O .bss	0000000000000008 __environ
000000000003b2d0 l     O .bss	0000000000000008 environ

위를 보면 알 수 있듯이 로더인 ld-linux-x86-64.so.2 에도 똑같은 __environ 이 존재하는 것을 알 수 있다.

그렇다면 __environ 은 언제 누구에 의해 결정되는 것일까?

Python
b _start // 로더가 시작할때 bp 설정
r

이렇게 한뒤

Python
pwndbg> p environ 
$1 = (char **) 0x0
pwndbg> p &environ
$2 = (char ***) 0x7ffff7ffe2d0 <environ>

보면 아직 값이 안정해져 있다. 그리고

Python
pwndbg> vmmap   
LEGEND: STACK | HEAP | CODE | DATA | WX | RODATA
             Start                End Perm     Size Offset File
    0x555555554000     0x555555555000 r--p     1000      0 /home/environ/environ/environ
    0x555555555000     0x555555556000 r-xp     1000   1000 /home/environ/environ/environ
    0x555555556000     0x555555557000 r--p     1000   2000 /home/environ/environ/environ
    0x555555557000     0x555555559000 rw-p     2000   2000 /home/environ/environ/environ
    0x7ffff7fbd000     0x7ffff7fc1000 r--p     4000      0 [vvar]
    0x7ffff7fc1000     0x7ffff7fc3000 r-xp     2000      0 [vdso]
    0x7ffff7fc3000     0x7ffff7fc5000 r--p     2000      0 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fc5000     0x7ffff7fef000 r-xp    2a000   2000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7fef000     0x7ffff7ffa000 r--p     b000  2c000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffff7ffb000     0x7ffff7fff000 rw-p     4000  37000 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
    0x7ffffffde000     0x7ffffffff000 rw-p    21000      0 [stack]
0xffffffffff600000 0xffffffffff601000 --xp     1000      0 [vsyscall]

보면 아직 libc는 올라와 있지도 않다.

Python
pwndbg> watch environ 
Hardware watchpoint 2: environ
pwndbg> c
Continuing.

Hardware watchpoint 2: environ

Old value = (char **) 0x0
New value = (char **) 0x7fffffffe698
_dl_sysdep_start (start_argptr=start_argptr@entry=0x7fffffffe680, dl_main=dl_main@entry=0x7ffff7fe48e0 <dl_main>) at ../elf/dl-sysdep.c:110

watch 명령어로 확인해 보니

Python
   0x00007ffff7fe246d <+45>:	mov    DWORD PTR [rip+0x1a655],edx        # 0x7ffff7ffcac8 <_dl_argc>
   0x00007ffff7fe2473 <+51>:	movsxd rdx,edx
   0x00007ffff7fe2476 <+54>:	lea    rdi,[rax+rdx*8+0x8]
   0x00007ffff7fe247b <+59>:	mov    QWORD PTR [rip+0x1be4e],rdi        # 0x7ffff7ffe2d0 <environ>
=> 0x00007ffff7fe2482 <+66>:	cmp    QWORD PTR [rdi],0x0

다음 위치에서 바뀜을 확인할 수 있었다. 즉, 로더에서 먼저 바뀌는 것은 확실하다. 로더가 이미 올라와 있는 프로그램을 통해 environ을 계산한뒤 결정한다. 그리고 이값이 그대로 libc에 있는 bss 영역에도 옮겨 진다.

즉, gdb로 계산하지 말고 그냥 objdump로 오프셋 계산이 가장 안전하고 확실하다.