Recent Posts
Recent Comments
Link
«   2024/12   »
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
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] stage4_shellcode만들기(open, execve) 본문

Hack/DreamHack(로드맵)

[System_Hacking] stage4_shellcode만들기(open, execve)

CIDY 2022. 7. 1. 04:30

마침 alphanumeric쉘코드 만들고 왔는데 드림핵에서 쉘코드를 만들어 보라길래... 그냥 인자 세팅 후 syscall만 해주면 되는거라 즐겁게 했다ㅎㅎㅎ

*open

/tmp/flag를 읽는 쉘코드를 만드는게 조건이었다.

char buf[0x30]

int fd = open("/tmp/flag", RD_ONLY, NULL);
read(fd, buf, 0x30);
write(1, buf, 0x30);


open 호출 -> read 호출 -> write호출

global _start

_start: 

        ;open
        push 0x67
        mov rax, 0x616c662f706d742f
        push rax
        mov rdi, rsp
        xor rsi, rsi
        xor rdx, rdx
        mov rax, 0x2
        syscall

        ;read
        mov rdi, rax
        mov rsi, rsp
        sub rsi, 0x30 ;buf 할당
        mov rdx, 0x30
        mov rax, 0x0
        syscall

        ;write
        mov rdi, 0x1
        mov rax, 0x1
        syscall

어셈블리 코드는 이러하다. 그냥 인자 잘 세팅해주고 syscall만 해주면 된다.

근데 처음에 8바이트 넘는거 모르고 무지성으로 레지스터에 /tmp/flag를 mov하려고 했는데 저렇게 나누어 저장할 수 있는지 몰랐다...

nasm으로 컴파일해주려는데 오류가 나서 드림핵에서 하라는대로 C코드로 어셈블리를 작성해주기로 했다.

__asm__(
    ".global run_sh\n"
    "run_sh:\n"
    "push 0x67\n"
    "mov rax, 0x616c662f706d742f \n"
    "push rax\n"
    "mov rdi, rsp\n"
    "xor rsi, rsi\n"
    "xor rdx, rdx\n"
    "mov rax, 2\n"
    "syscall\n"
    "\n"
    "mov rdi, rax\n"
    "mov rsi, rsp\n"
    "sub rsi, 0x30\n"
    "mov rdx, 0x30\n"
    "mov rax, 0x0\n"
    "syscall\n"
    "\n"
    "mov rdi, 1\n"
    "mov rax, 0x1\n"
    "syscall\n"
    "\n"
    "xor rdi, rdi\n"
    "mov rax, 0x3c\n"
    "syscall");
void run_sh();
int main() { run_sh(); }


컴파일 옵션 : gcc open.c -o open -masm=intel


vi로 디렉토리 지정해서 대충 아무말이나 써줬는데 잘 출력된다.

이걸 gdb로 보면...


rsp, rdi에 인자가 들어있는 모습을 볼 수 있다. 레지스터는 당연히 8바이트 이상의 값을 못 담는다. 근데 만약
QWORD PTR[rsp] 했으면 /tmp/fla 로 잘려 나오겠지만 rsp자체는 문자열을 담은 주소이고, 그걸 rdi에 넘겨준 것이므로 이렇게 나타난다.

b* run_sh 해주고 run시키면 딱 rip가 만든 코드 시작부분에 가 있다.

rip를 첫 번째 syscall까지 이동시켜보면


이 사진에는 안 나와 있는데 rax == 0x2라서 open호출하는거다. 인자가 잘 들어가 있는 것을 볼 수 있다. (RD_ONLY == 0)


그리고 /tmp/flag를 안만들어 놨더니 open반환값으로 -2가 fd에 들어가서 다시 디버깅했다...ㅎㅎ fd == 3 이 rax에 잘 들어가 있는 것 확인 가능. (rax는 함수의 반환값을 저장하는 레지스터다.)


디버깅하다 귀찮아서 걍 run시켰는데 내가 적어준 flag가 (아까랑 조금 바꿈) 잘 출력되는 것을 확인 가능하다. 물론 뒤에 이상한것도 딸려나왔지만...0x30만큼 읽어왔으니 당연...


*execve

execve는 많이 봐왔겠지만...execve("/bin/sh", NULL, NULL)이다. 참고로 이때까지 /bin/sh를 무지성으로 썼을텐데 (내가 그랬다 ㅋㅋ) 리눅스에서는 기본 실행 프로그램들이 /bin 디렉토리에 있고 sh도 그중 하나다.


x86_64에서 execve호출번호는 59번이다. 이걸 참고해서 어셈블리부터 짜 보자.


이렇게 대충 짜 줬고 nasm으로 컴파일해서 실행되는 것 까지 확인했다. (드림핵에서는 자꾸 스켈레톤 코드 만들어서 gcc로 컴파일해주는데 그냥 귀찮고 잘 실행되는 거 확인했으니 여기까지...)

-> 암튼 이걸 덤프떠서 기계어로 된 걸 가져오면 그게 쉘이 실행되는 코드 조각, 쉘코드인거임.

- >근데 저걸 어셈블리 실행시키면 쉘 열리는데 쉘코드로 쓰진 못할거임ㅋㅋ 걸리는 부분이 너무 많아서