Pwnable.kr - input

3 minute read

Challenge description:

Mom? how can I pass my input to a computer program?

#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;
}

The challenges tests your ability to feed input to different sources.

Stage 1 (command line args):

  • argc (num of arguments) = 100
  • argv[‘A’] = argv[65] = “\x00”
  • argv[‘B’] = argv[66] = “\x20\x0a\x0d”

Stage 2 (stdin and stderr):

  • stdin = “\x00\x0a\x00\xff”
  • stderr = “\x00\x0a\x02\xff”

Stage 3 (environment variables):

  • “\xde\xad\xbe\xef” = “\xca\xfe\xba\xbe”

Stage 4 (file input):

  • content of file with name “\x0a” = “\x00\x00\x00\x00”

Stage 5 (network input):

  • send “\xde\xad\xbe\xef” to the connection listening on port (argv[‘C’] = argv[67])

We can only create new files at /tmp (for stage 4).

For some reason, pwn tools doesn’t work at /tmp, so we create a subfolder and cd to it.

If we change directory to /tmp/folder_name, the binary will not be able to read “flag” as it’s not in the same directory, so we create a symbolic link to it with “ln -s”.

Solution:

# solve.py

from pwn import *
import os

args = ['A']*100
args[65] = '\x00'
args[66] = '\x20\x0a\x0d'
args[67] = '4444'

r1, w1 = os.pipe()
r2, w2 = os.pipe()
os.write(w1, '\x00\x0a\x00\xff')
os.write(w2, '\x00\x0a\x02\xff')

with open('\x0a', 'w') as f:
	f.write('\x00\x00\x00\x00')

p = process(executable='/home/input2/input', 
	    argv=args, 
	    stdin=r1, stderr=r2, 
	    env={'\xde\xad\xbe\xef' :'\xca\xfe\xba\xbe'})

conn = remote('localhost', 4444)
conn.sendline('\xde\xad\xbe\xef')

p.interactive()
$ cd /tmp
$ mkdir cjmpql
$ cd cjmpql
$ nano solve.py	     # write the solution to this file
$ ln -s /home/input2/flag flag
$ python solve.py 
[+] Starting local process '/home/input2/input': pid 114109
[+] Opening connection to localhost on port 4444: Done
[*] Switching to interactive mode
[*] Process '/home/input2/input' stopped with exit code 0 (pid 114109)
Welcome to pwnable.kr
Let's see if you know how to give input to program
Just give me correct inputs then you will get the flag :)
Stage 1 clear!
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
Mommy! I learned how to pass various input in Linux :)

Flag: Mommy! I learned how to pass various input in Linux :)