Pwnable.kr - input
Write-up for input of pwnable.kr
Write-up ref: this blog
0x00 Puzzle
Mom? how can I pass my input to a computer program?
ssh input2@pwnable.kr -p2222 (pw:guest)
This is really a great puzzle to learn and practice Linux input programming skill.
Covered 5 different possible types of inputting to a program.
Beginners like me can learn a lot from this puzzle, especially solve it only with the help of Google but not direct write-ups.
0x01 Explore - Overview
$ ssh input2@pwnable.kr 2222
input2@ubuntu:~$ ls -l
total 24
-r--r----- 1 input2_pwn root 55 Jun 30 2014 flag
-r-sr-x--- 1 input2_pwn input2 13250 Jun 30 2014 input
-rw-r--r-- 1 root root 1754 Jun 30 2014 input.c
Same as usual, pwn the binary file to cat the flag.
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;
}
There are 5 stages before the /bin/cat flag
, seems no other easy way to bypass them.
0x02 Stage1 - 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");
- First, we need to pass 100 arguments to the binary.
- Second, number 65 (=ord('A')) argument must be \x00 (one byte)
- Third, number 66 (=ord('B')) argument must be \x20\x0a\x0d (three byte)
My very first idea to pass those checking is simply:
./input `python -c "print 'A ' * 64 + '\x00 \x20\x0a\x0d' + ' A'*(99-64-2)"`
But it didn't work, then I printed out the argv and found that \x00 cannot be passed to the program successfully since it represent the end of a string.
Then I tried two ways in python
subprocess.call(['./input', arg1, arg2, ...])
subprocess.call("./input "+payload, shell=True)
Both threw error and said arguments should contain only strings.
So I started google and found the write-up linked at the top of this page, where I learned how to execute binary with arguments in C. There are no such limits in C like python to the arguments.
Here comes the core code for Stage 1:
#include <unistd.h>
char *argv[101] = {[0 ... 99]="A"};
argv['A'] = "\x00";
argv['B'] = "\x20\x0a\x0d";
execve("./input", argv);
Here I learned a new gesture to set value for char* when defined it.
Stage 1 done.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main(){
char *argv[101] = {[0 ... 99]="A"};
char *envp[2]={"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"};
argv['A'] = "\x00";
argv['B'] = "\x20\x0a\x0d";
argv['C'] = "81235";
int pipe1[2], pipe2[2];
if(pipe(pipe1)==-1||pipe(pipe2)==-1){
printf("error pipe\n");
exit(1);
}
if(fork() == 0){
dup2(pipe1[0],0);
close(pipe1[0]);
close(pipe1[1]);
dup2(pipe2[0],2);
close(pipe2[0]);
close(pipe2[1]);
execve("./input", argv, envp);
}else{
write(pipe1[1], "\x00\x0a\x00\xff", 4);
write(pipe2[1], "\x00\x0a\x02\xff", 4);
struct sockaddr_in saddr;
int sock = socket(AF_INET, SOCK_STREAM, 0);
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
//saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons( atoi(argv['C']) );
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sleep(5);
connect(sock, (struct sockaddr *) &saddr, sizeof(saddr));
send(sock, "\xde\xad\xbe\xef", 4, 0);
close(sock);
}
return 0;
}
(to be continued)