pwnablr.kr Todder's Bottle:input
なんかかなり放置していたかつ日本語解説無かった(するまでも無いかもしれないけど)ので。
問題
#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; }
まあプログラムを頑張って下まで通すだけです。
解説
Pythonをsshの内部で動かしています(他の人見るとpwntool使っている人もいますが)
Stage1
argvですね。subprocess.callを使うと、中を配列で受け取れます。"\x00"は使えず、代わりに""が使えます。面倒だったのでargv配列を100個用意してから左端を削るチキンプレー。
import subprocess # Stage 1 arg=["a" for i in range(100)] arg[ord("A")]="" arg[ord("B")]="\x20\x0a\x0d" s=subprocess.call(["/home/input2/input"]+arg[1:])
Stage2
入出力。subprocess.callでは入出力を指定することが出来ます。ただ、ファイルオブジェクトを渡す必要があるみたいで、適当にファイル作って送りましょう。
import subprocess # Stage 1 arg=["a" for i in range(100)] arg[ord("A")]="" arg[ord("B")]="\x20\x0a\x0d" # Stage 2 with open("stdin.txt","w") as f: f.write("\x00\x0a\x00\xff") with open("stderr.txt","w") as f: f.write("\x00\x0a\x02\xff") s=subprocess.Popen(["/home/input2/input"]+arg[1:],stdin=open("stdin.txt","r"),stderr=open("stderr.txt","r"))
Stage3
envです。osをインポートしましょう。ググれば出ます。
import subprocess import os # Stage 1 arg=["a" for i in range(100)] arg[ord("A")]="" arg[ord("B")]="\x20\x0a\x0d" # Stage 2 with open("stdin.txt","w") as f: f.write("\x00\x0a\x00\xff") with open("stderr.txt","w") as f: f.write("\x00\x0a\x02\xff") # Stage 3 os.environ["\xde\xad\xbe\xef"]="\xca\xfe\xba\xbe" s=subprocess.Popen(["/home/input2/input"]+arg[1:],stdin=open("stdin.txt","r"),stderr=open("stderr.txt","r"))
Stage4
まさかのファイル入出力。さっきやりました。
import subprocess import os # Stage 1 arg=["a" for i in range(100)] arg[ord("A")]="" arg[ord("B")]="\x20\x0a\x0d" # Stage 2 with open("stdin.txt","w") as f: f.write("\x00\x0a\x00\xff") with open("stderr.txt","w") as f: f.write("\x00\x0a\x02\xff") # Stage 3 os.environ["\xde\xad\xbe\xef"]="\xca\xfe\xba\xbe" # Stage 4 with open("\x0a","w") as f: f.write("\x00\x00\x00\x00") s=subprocess.Popen(["/home/input2/input"]+arg[1:],stdin=open("stdin.txt","r"),stderr=open("stderr.txt","r"))
Stage5
一番難しいのはこれっぽさそう。インターネット通信とPythonといえばこのページを思い出したのは私だけでしょうか。自分だけですね。
ってことで適当にsocketを送ります。IPアドレスはエディタから戻って「ifconfig」するとループバックアドレス(自分自身を指すアドレス)が127.0.0.1となっているのでそれを使います。ポートはargv['C']らしいので適当な数値をつけましょう。
で、そのまま実行しても何も起きません。ここで、今まで使っていたsubprocess.callなのですが、これは並列処理をするものでは無いので、代わりにPopenに変えます。で、これでも恐らく何も起きないです。出力があまりに早いと受け取ってくれないので少し待ちます。
import subprocess import os import socket import time # Stage 1 arg=["a" for i in range(100)] arg[ord("A")]="" arg[ord("B")]="\x20\x0a\x0d" # Stage 2 with open("stdin.txt","w") as f: f.write("\x00\x0a\x00\xff") with open("stderr.txt","w") as f: f.write("\x00\x0a\x02\xff") # Stage 3 os.environ["\xde\xad\xbe\xef"]="\xca\xfe\xba\xbe" # Stage 4 with open("\x0a","w") as f: f.write("\x00\x00\x00\x00") # Stage 5 arg[ord("C")]="41748" s=subprocess.Popen(["/home/input2/input"]+arg[1:],stdin=open("stdin.txt","r"),stderr=open("stderr.txt","r")) time.sleep(3) host="127.0.0.1" port=41748 # anything is ok if equal to argv["C"] client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((host,port)) client.send("\xde\xad\xbe\xef")
これで5ステージ全て撃破出来ました。
Extra
実はこの後に自明な罠があって、そこに30分ほど躓いてました。flagの呼び出しはsystem("bin/cat flag")にて行われます。pythonの実行は(僕の場合)/tmp/fiordで行われていて、この時当然/tmp/fiord/flagを探します。なのでシンボリックリンクを貼っていなくてはなりません。ただ、ファイルが無くても何もエラーを吐いてくれないので、つらかった。。。
ln -s /home/input2/flag