과제

4주차

werty2 2025. 4. 10. 12:04

What i did

1. 포너블에 대해 배웠다.

2. 우분투를 통해 관련 문제들을 풀었다.

 

What I learn

-포너블이란 운영체제나 소프트웨어, 하드웨어에 내재된 보안 취약점을 해킹하는 것이다. 포너블의 목표는 관리자 권한을 탈취해 그 프로그램의 쉘을 따내는 것이다. 

 

-어셈블리어를 통해 취약적이 있는 공격 대상을 리버싱을 분석해 기계어를 의사코드 형태로 복원하고 메모리 레벨이서 이해할 수 있다. x86-64 는 64비트 컴퓨터에서 사용하는 CPU 아키텍처이다. 기존 32비트(x86) 아키텍처를 기반으로 64비트 확장한 것이다. 같은 명령어라도 x86-64 에서만 쓰이는 것이 있어 실제로 사용할 때는 찾아보고 사용해야할 것 같다.

스택은 임시 기억 공간, 함수 호출/리턴과 지역변수 저장소이다. 스택의 구조는 Last-In-First-Out (LIFO) 이므로 나중에 들어온 것이 먼저 나간다. 스택에 데이터를 넣는 것을 push, 값을 추출하는 것을 pop 이라고 하며 일반적으로 top에 현재 스택 포인터가 사용된다.

 

시스템 콜이란 커널 모드( 전체 시스템을 제어하기 위해 시스템 소프트웨어에게 부여하는 권한 ) 과 유저모드( 사용자에게 부여하는 권한 )  사이에서 사용자 프로그램이 운영체게의 서비스를 받기 위해 커널 함수를 호출하는 것이다. 시스템 콜을 요청하기 위해서는 떤 시스템콜을 쓸지 rax에 번호를 넣고, 인자들을 rdi, rsi, rdx, r10, r8, r9, stack 순서대로 넣는다.

 

-운영체제는 하드웨어와 응용 프로그램 사이의 인터페이스 역할을 하는 시스템 소프트웨어 이다. 기능으로는 프로세스 관리, 메모리 관리, 파일 시스템 관리, 입출력 장치 관리, 보안, 네트워킹 등이 있다. 운영체제는 사용자와 하트웨어 사이의 직접적인 상호작용 없이도 컴퓨터를 사용할 수 있게 해주는 역할을 한다.

쉘은 운영체제에서 사용자와 컴퓨터 하드웨어 사이를 중계해주는 인터페이스로, 사용자가 입력한 명령어를 운영체제에서 이해할 수 있는 형식으로 변환하고, 컴퓨터 하드웨어를 이용해 명령을 실행시켜주는 역할을 수행한다. 터미널 창에서 실행되며, 일반적으로 명령어 해석기와 함께 제공된다.

커널은 운영체제의 핵심 부분으로, 하드웨어와 소프트웨어 간의 인터페이스 역할을 수행한다. 운영체제에 따라 다른 디자인과 구현을 가지며 각각의 운영체제에서 필요한 기능과 특성을 반영하여 설계되어있다.

하드웨어-커널-쉘-사용자 순으로 상호작용을 한다.

 

시스템 해킹의 목표는 관리자의 쉘을 얻어 권한을 얻는 것이다. 이러한 쉘을 열기 위해서는 함수들이 필요한데, 이러한 함수들을 바이트 코드로 옮긴 것이 쉘 코드이다. 쉘 코드는 작은 크기의 코드로 소프트웨어 취약점을 이용해 관리자 권한을 탈취하기 위해 사용된다. 쉘 코드의 종류로는 orw 쉘 코드와 execve 쉘 코드가 있다. orw 는 특정 파일을 열고, 읽고, 출력하는 코드이고 execve 는 임의의 프로그램을 실행하는 쉘 코드로 일반적으로 사용된다. 최신 리눅스는 대부분 sh, bash를 기본 쉘 프로그램이로 가지고 있어 execve()로 sh를 실행할 수 있다.

 

 

실습

1. bof

먼저, 우분투에서 다운로드 받은 파일을 열어서 objdump -d bof 라는 명령어를 실행해주면 다음과 같이 뜬다. 여기서 여러 함수들의 호출에 대해서 볼 수 있다. 그리고 여기서 0x80 이라는 값을 통해 버퍼의 크기가 128바이트라는 것을 알 수 있다.

버퍼를 채우기 위해 파일을 열고 아무 문자열 하나를 128개 만큼 입력했다. 그 다음 문제 서버에 연결하기 위해 nc 서버주소를 입력하면 flag 값이 나온다.

 

2. baby-bof

nc 서버주소를 입력하니 win 함수의 주소가 0x40125b 이고, 현재 프로그램의 메인 함수에서는 호출되지 않는다는 점을 알려준다. 그 후 name을 적도록 커서가 깜빡이는데, 아무 이름이나 입력한 후 엔터를 친다. 그러면 여러 주소와 값이 나온 뒤에 hex value를 치도록 한다. 거기에 아까 봤던 win() 함수 주소를 입력한다. 그 후 리턴 주소까지 도달하기 위해 6 이상의 수를 integer count 뒤에 입력하면 

복귀함수가 win() 함수로 바뀌게 되며 flag가 출력되게 된다.

 

 

 

Weekly Challenge

basic_exploitation_001: 이 코드를 읽어보니 입력을 받을 때 길이 제한이 없는 gets()를 사용하고, gets(buf)에서 지역변수인 buf는 지역변수이고 이는 스택에 저장되므로 버퍼 오버플로우 취약점이 있는 C 프로그램이라는 것을 알 수 있다. 그래서 오버플로우로 리턴 주소를 플래그 파일 내용을 출력하는 read_flag() 함수의 주소로 바꾸면 플래그를 출력할 수 있다고 생각이 든다.

 

먼저 프로그램이 몇 비트인지 알아보기 위해 file ./basic_exploitation_001 이라고 입력했다. 결과를 통해 32비트라는 것을 알 수 있다.

 

그 다음, 파일을 다운로드 받아 터미널을 실행시킨 후 objdump -d basic_exploitation_001 명령어를 입력하면

이 부분을 확인할 수 있다. 여기서 add $0xfffffff80,%esp 는 실제로 sub $0x80, %esp 와 같고, 이는 C 코드에서 char buf[0x80] 로 해석될 수 있다. 그래서 buf가 스택에 128바이트를 할당한다는 사실을 알 수 있다.

 

그리고 아래 사진의 <read_flag> 앞에 적힌 0x080485b9 가 get_flag() 함수의 주소라는 것을 알 수 있다.

스택에 128바이트를 할당하므로 버퍼를 꽉 채우기 위해 A를 128개 적고 난 후 다시 원래 함수 주소로 돌아가지 못하게 리턴 주소를 가리키는 포인터인 EBP를 채우기 위해 B를 4개 적는다. 그리고 마지막에는 get_flag() 함수의 주소인 0x080485b9 를 바이트 단위로 쪼갠 \xb9\x85\x04\x08 으로 표현해준다. 왜냐하면 32비트에서는 주소를 4바이트씩 거꾸로 저장하는 리틀 엔디안 방식을 사용하기 때문이다. 참고로 32비트에서는 모든 주소가 4비트라서 EBP도 4비트이고, 어떤 것을 덮는지에 따라 A와 B로 구분했다. 이렇게 채운 파일을 payload 라는 이름으로 내가 현재 있는 위치에 생성한다.

그 후에 cat으로 방금 생성한 파일을 출력해서 드림핵에서 생성한 서버에 입력으로 준다.

 

그러면 서버는 스택이 덮히고 리턴 주소가 바뀌면서 get_flag() 함수가 실행되고, 결국 flag가 출력되게 된다.

 

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

basic_exploitation_000: 이 프로그램은 char buf[0x80] 라는 코드를 통해 함수 스택 프레임 안에 크기가 buf 128인 배열이고, scanf("%141s", buf) 는 최대 141 바이트를 크기가 128인 buf에 저장한다는 의미인데, 버퍼보다 더 많은 데이터를 입력받고 있으니 스택의 다음 데이터들을 덮는 오버플로우가 발생할 수 있도록 만들 수 있겠다는 생각이 든다. 내 생각으로는 scanf("%141s", buf) 가 아까 봤던 get() 함수와 비슷한 것 같다. get() 함수는 길이 제한이 없고, scanf("%141s", buf)는 길이 제한이 있긴 하지만 그 제한이 버퍼보다 커 비슷한 방식으로 오버플로우 가능성을 만든다고 생각하기 때문이다.

프로그램은 32비트라는 사실을 알 수 있다.

 

파일을 다운로드 받아 그 위치에서 터미널을 실행시키고 objdump -d basic_exploitation_000 를 입력하고  main 함수를 살펴보면 80485dc: 83 c4 80 add $0xffffff80, %esp 이 줄을 보면, 여기서 add $0xffffff80는 sub $0x80과 같은 의미이므로 스택에서 가장 위인 %esp 부터 위에서부터 아래로 128바이트만큼 확장한다 즉, 스택에서 128바이트만큼 공간을 확보한다는 의미라는 것을 알 수 있다. 그러므로 buf+ebp=132바이트를 입력하면 리턴 주소를 덮게 된다는 것을 생각할 수 있다.

 

생성한 서버에 연결한 후 버프 주소가 보인다. 이 버프 주소는 연결을 끊고 다시 연결할 때마다 매번 바뀐다. 그러므로 끊지 않고 입력하기 위해서 새 터미널 창을 하나 더 열었다.

새롭게 연 터미널 창에서 26비트 쉘코드를 payload라는 이름을 가진 파일에 먼저 입력한다. 그 다음 a를 106개 만큼 입력한 값을 파일에 추가한다. 132-26=106 이므로 106개를 입력하였다. 그러고는 버프주소를 리틀 엔디안 방식으로 바꿔 파일에 추가 입력을 해 주었다. 그러고 wc를 이용해 총 26+106+4=136 바이트가 잘 입력된 것을 확인하였다.

 

 

Issues

그러고 나서 cat으로 쉘을 출력해서 서버 주소로 보내주었다. 그리고 열린 쉘에서 ls 와 read_flag, ./read_flag 등을 입력하였다. 하지만 아무런 값도 나오지 않았다. 분명 gpt가 하라는대로 다 했는데.. 그냥 커서가 깜빡이기만 할 뿐 어떠한 진전도 없었다. 여러 번 물어보고 여러 번 입력해봐도 결과는 바뀌지 않았다. 

'과제' 카테고리의 다른 글

5주차  (0) 2025.05.11
3주차  (0) 2025.04.06
2주차  (0) 2025.03.31
1주차  (0) 2025.03.24