how to use stdout & stdin & stderr to gadget by stack pivoting

해당 글을 읽고 오면 이해하기 편하다.

Assembly
.bss:0000000000601020 ; ===========================================================================
.bss:0000000000601020
.bss:0000000000601020 ; Segment type: Uninitialized
.bss:0000000000601020 ; Segment permissions: Read/Write
.bss:0000000000601020 _bss            segment para public 'BSS' use64
.bss:0000000000601020                 assume cs:_bss
.bss:0000000000601020                 ;org 601020h
.bss:0000000000601020                 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:0000000000601020                 public stdout
.bss:0000000000601020 ; FILE *stdout
.bss:0000000000601020 stdout          dq ?                    ; DATA XREF: LOAD:0000000000400380↑o
.bss:0000000000601020                                         ; sub_4005E0+1↑o ...
.bss:0000000000601020                                         ; Copy of shared data
.bss:0000000000601028                 align 10h
.bss:0000000000601030                 public stdin
.bss:0000000000601030 ; FILE *stdin
.bss:0000000000601030 stdin           dq ?                    ; DATA XREF: LOAD:0000000000400398↑o
.bss:0000000000601030                                         ; init_0+4↑r
.bss:0000000000601030                                         ; Copy of shared data
.bss:0000000000601038 byte_601038     db ?                    ; DATA XREF: sub_400650↑r
.bss:0000000000601038                                         ; sub_400650+12↑w
.bss:0000000000601039                 align 20h
.bss:0000000000601039 _bss            ends
.bss:0000000000601039

알다 시피 bss/data 영역에는 stdout , stdin , stderr의 포인터 변수가 존재한다. 여기에는 실제 glibc의 주소가 들어가 있기에 leak을 하기 상당히 좋다. 근데 읽을 수 있는 함수가 아예 없다면, 이 값을 레지스터라도 사용할 수 있으면, rsi나 rdi 레지스터에 넣어서 각각 read() , gets() 와 같은 입력함수로 FSOP 공격으로 이어갈 수 있다.

그런데 생각을 해보면 저기에 존재하는 값을 사용할려면 ROP를 통해 해야하는데 그럴려면 stack을 저 위치로 옮겨야 한다. 따라서 위에 미리 서술한 stack pivoting이라는 기술을 사용해서 스택을 옮겨야 한다.

근데 더 중요한 문제가 있는데 그전에 ROP 코드를 stdout 와 같은 타겟 영역을 제외해서 잘 이어나가야 한다.

즉, 지금 보면 목표가 read(0, stdout , 0x400 )이라면

0x601018 위치에는 pop rsi 가젯의 주소를 넣고 ( 8바이트 만큼 )

0x601020 위치는 보존해야한다. 0x601028 부터 다시 pop rdi , pop rdx 와 같은 가젯을 이어 붙여야 한다.

즉, 앞뒤로 따로 따로 가젯을 넣어서 이어붙이면 stdout의 값을 레지스터로 사용할 수 있게 된다.이는 FSOP 공격으로 이어갈 수 있다.