Wargame/pwnable.kr
input
wyv3rn
2022. 12. 28. 17:22
728x90
반응형
1. intro
2. code 및 분석
2.1. code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(int argc, char* argv[], char* envp[]){
printf("Welcome to pwnable.kr\n");
printf("Let's see if you know how to give input to program\n");
printf("Just give me correct inputs then you will get the flag :)\n");
// argv
if(argc != 100) return 0;
if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
printf("Stage 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
printf("Stage 2 clear!\n");
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
// file
FILE* fp = fopen("\x0a", "r");
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0;
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
fclose(fp);
printf("Stage 4 clear!\n");
// network
int sd, cd;
struct sockaddr_in saddr, caddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
printf("bind error, use another port\n");
return 1;
}
listen(sd, 1);
int c = sizeof(struct sockaddr_in);
cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
if(cd < 0){
printf("accept error, tell admin\n");
return 0;
}
if( recv(cd, buf, 4, 0) != 4 ) return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
printf("Stage 5 clear!\n");
// here's your flag
system("/bin/cat flag");
return 0;
}
2.2. 분석
코드 길이가 다소 길다;;;
어쨌든 조건을 하나하나 만족하면 플래그를 준다.
3. 취약점 확인 및 공격 준비
3.1. 취약점
별도로 취약점은 없다.
3.2. 공격 준비
하나씩 만족시켜보자.
첫번째는 argv이다.
// argv
if(argc != 100) return 0;
if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
printf("Stage 1 clear!\n");
인자 수가 100개여야 하고, argv A 즉, 65번째가 0x00, 66번째가 0x0d0a20이면 된다.
즉, 아래와 같이 클리어.
from pwn import *
pay=['./input']
for i in range(1,100):
if (i == 66):
pay.append(b'\x20\x0a\x0d')
else:
pay.append(b'\x00')
s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
p = s.process(pay)
p.interactive()
두번째는 read 이다.
// stdio
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
printf("Stage 2 clear!\n");
첫번째 read는 atdin으로 받아들이며, 값이 0xff000a00 이면 통과,
두번째 read는 stderr 값이 0xff020a00 이면 통과이다.
stderr 값을 어떻게 줘야하나 고민했는데, gdb로 보니 그냥 주소를 참조하기에 값을 던졌다.
여기까지 보면 아래와 같다.
from pwn import *
pay=['./input']
for i in range(1,100):
if (i == 66):
pay.append(b'\x20\x0a\x0d')
else:
pay.append(b'\x00')
s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
p = s.process(pay)
p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))
p.interactive()
세번째는 환경변수다.
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
프로그램 실행 시 함께 던질 수 있으니 변수로 등록해서 던지자.
from pwn import *
pay=['./input']
for i in range(1,100):
if (i == 66):
pay.append(b'\x20\x0a\x0d')
else:
pay.append(b'\x00')
env_={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
p = s.process(pay,env=env_)
p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))
p.interactive()
네번째는 파일 오픈이다.
// file
FILE* fp = fopen("\x0a", "r");
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0;
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
fclose(fp);
printf("Stage 4 clear!\n");
경로 지정이 필요하기에 어쩔 수 없이 코드를 다소 변경하였다.
from pwn import *
arg=[]
for i in range(0,100):
if (i == 66):
arg.append(b'\x20\x0a\x0d')
else:
arg.append(b'\x00')
env_={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
#p = remote('pwnable.kr',9000)
s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))
p.interactive()
마지막 다섯번째 소캣이다.
// network
int sd, cd;
struct sockaddr_in saddr, caddr;
sd = socket(AF_INET, SOCK_STREAM, 0);
if(sd == -1){
printf("socket error, tell admin\n");
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
printf("bind error, use another port\n");
return 1;
}
listen(sd, 1);
int c = sizeof(struct sockaddr_in);
cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
if(cd < 0){
printf("accept error, tell admin\n");
return 0;
}
if( recv(cd, buf, 4, 0) != 4 ) return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
printf("Stage 5 clear!\n");
코드는 길지만 결국은 소캣을 열고, 지정된 포트 즉 argv['C']로 접속되어야하고,
접속 후 받아들인 값이 \xde\xad\xbe\xef 여야 한다.
from pwn import *
arg=[]
for i in range(0,100):
if (i == 66):
arg.append(b'\x20\x0a\x0d')
elif (i == 67):
arg.append(b'22222')
else:
arg.append(b'\x00')
env_={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
#p = remote('pwnable.kr',9000)
s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))
sleep(3)
soc = s.remote('localhost',22222)
soc.send('\xde\xad\xbe\xef')
p.interactive()
4. exploit
위 까지 작성 후 아래와 같이 메시지가 return 되었다.
┌[WyV3rN]-(d/hack)-
└> python3 a.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] fd@pwnable.kr:
Distro Ubuntu 16.04
OS: linux
Arch: amd64
Version: 4.4.179
ASLR: Enabled
/mnt/d/hack/a.py:18: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
/mnt/d/hack/a.py:20: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
[+] Starting remote process bytearray(b'/home/input2/input') on pwnable.kr: pid 98694
[+] Connecting to localhost:22222 via SSH to pwnable.kr: Done
/mnt/d/hack/a.py:29: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
soc.send('\xde\xad\xbe\xef')
[*] Switching to interactive mode
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
/bin/cat: flag: No such file or directory
[*] Got EOF while reading in interactive
$
flag 파일은 심볼릭 링크하자.
input2@pwnable:/tmp/wyv3rn$ ln -s /home/input2/flag flag
input2@pwnable:/tmp/wyv3rn$ ls -al
total 1040420
drwxrwxr-x 2 input2 input2 4096 Dec 28 03:20 .
drwxrwx-wt 11 root root 1065357312 Dec 28 03:21 ..
-rw-rw-r-- 1 input2 input2 4 Dec 28 03:21 ?
lrwxrwxrwx 1 input2 input2 17 Dec 28 03:20 flag -> /home/input2/flag
┌[WyV3rN]-(d/hack)-
└> python3 a.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] fd@pwnable.kr:
Distro Ubuntu 16.04
OS: linux
Arch: amd64
Version: 4.4.179
ASLR: Enabled
/mnt/d/hack/a.py:18: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
/mnt/d/hack/a.py:20: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
[+] Starting remote process bytearray(b'/home/input2/input') on pwnable.kr: pid 112819
[+] Connecting to localhost:22222 via SSH to pwnable.kr: Done
/mnt/d/hack/a.py:28: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes
soc.send('\xde\xad\xbe\xef')
[*] Switching to interactive mode
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
----------#플래그는 삭제
[*] Got EOF while reading in interactive
$
728x90
반응형