DockerMaze maze challenge write-up

Source: Internet
Author: User

DockerMaze maze challenge write-up

November 16 this year ~ On the 17th, in Dockercon eu 2015 held in barsarona, Schibsted Team released a DockerMaze challenge, which is similar to the game running out of the maze in 1990s. In this game, you wake up in the middle of the maze. You must escape from the maze to survive. On the console, you can use commands to perform interactive operations on the environment.

In this article, I will introduce how I got rid of the bitter sea.

We learned from the help command that there are "look", "interact" and "escape" commands. If you type "look front" in the game, it will report that there are signs on the wall, then execute "inspect wall" to give you some clues:

Found rooms:  - schibstedchallenge/dockermaze-weisse:latest  - schibstedchallenge/dockermaze-stout:latest  - schibstedchallenge/dockermaze-porter:latest  - schibstedchallenge/dockermaze-ipa:latestFound Keys:  - FollowTheWhiteRabbitFollowed path:  - Input: https://challenge.schibsted.com/assets/data/ct1.bin  - Output: ?More than a year and I'm still here. I'm loosing all hope. Maybe there is another key?

Quickly detects binary files, but does not get more information

 

Download the image and perform detection.

docker pull schibstedchallenge/dockermaze-weisse:latestdocker pull schibstedchallenge/dockermaze-stout:latestdocker pull schibstedchallenge/dockermaze-porter:latestdocker pull schibstedchallenge/dockermaze-ipa:latest

Have fun with this docker Image

WEISSE

docker inspect schibstedchallenge/dockermaze-weisse:latest

Related information

"Entrypoint": [ "/usr/local/bin/start.bash" ]"ExposedPorts": { "1954/tcp": {} }

 

It seems that there is a ruby application (weisse. rb) Listening to port 1954/tcp, which is executed through the "start. bash" bash script. In the additional information in the script, we know that:

# We use eureka + prana for service discovery.

This clue will be useful in the future

The weisse. rb file exposes the data received by a REST endpoint (/turing) Running the n'negecode cryptographic machine. In addition, in order to set up the Nenge cryptographic machine, it tries to obtain more information for DNS requests. The following is a code snippet:

...SNIP...BFBASE = 'aaa'set :bind, '0.0.0.0'set :port, 1954post '/turing' do data = request.body.read rotors = get_rotors('porter')plugboard = Hash[*PLUGBOARD.pack('H*').split('')] plugboard.merge!(plugboard.invert) rotors.map! do |r| Hash[[r].pack('H*').split('').zip((0...256).map{|i| i.chr})] end reflector = Hash[*REFLECTOR.pack('H*').split('')] reflector.merge!(reflector.invert) enigma(data, plugboard, rotors, reflector)end...SNIP...def get_rotors(nameserver) rotors = []Resolv::DNS.open({:nameserver=>[nameserver]}) do |r| ctr = 0loop do begin n = r.getresource("walzen-#{ctr}.dockermaze", Resolv::DNS::Resource::IN::TXT).data.to_i rescue Resolv::ResolvError break endbf = BFBASE.dup found_chunks = 0 rotors[ctr] = ''while found_chunks < n begin ck = r.getresource("walzen-#{ctr}-#{bf}.dockermaze", Resolv::DNS::Resource::IN::TXT).data.delete('"') rotors[ctr] << ck found_chunks += 1 rescue Resolv::ResolvError next ensure bf.next! end endctr += 1 end endrotorsend...SNIP...

STOUT

docker inspect schibstedchallenge/dockermaze-stout

Related information

"Entrypoint": [ "/usr/local/bin/stout.py" ]"ExposedPorts": { "31337/tcp": {} }

An error occurred while trying to run the docker image.

 

Because of this error, we cannot open this container. Changed the policy and wrote a Python file:

docker run -ti --entrypoint /bin/bash --name stout schibstedchallenge/dockermaze-stoutdocker cp stout:/usr/local/bin/stout.py .

Stout. py:

#!/usr/bin/env pythonimport osimport sysimport socketimport base64from datetime import datetimefrom dns import resolverfrom flask import Flask, request, make_responseapp = Flask('stout')PORTER_HOST = os.getenv('PORTER_PORT_53_TCP_ADDR')def xor(data, key): return "".join(map(lambda i: chr(ord(data[i]) ^ ord(key[i%len(key)])), xrange(len(data))))def transform(data): s = socket.socket()s.connect(('ipa', 6060)) s.sendall(base64.b64encode(data) + "\n") ret = s.makefile().readline().decode('base64') s.close() return ret@app.route("/gate", methods=['POST'])def gate(): t1 = datetime.now()data = request.stream.read()dns_resolver = resolver.Resolver()dns_resolver.nameservers = [PORTER_HOST] dns_answer = dns_resolver.query('bitwise.dockermaze', 'TXT') secret = dns_answer[0].to_text().strip('"')ret = transform(xor(data, secret))t2 = datetime.now()resp = make_response(ret, 200) resp.headers.extend({'X-Dockermaze-Time': t2-t1})return respif __name__ == '__main__': if not PORTER_HOST: sys.exit('error: cannot get key') app.run(host='0.0.0.0', port=31337)

The script publishes a REST endpoint to receive password data, obtain the porter host through DNS (the IP address needs to be provided through the environment variable), and send the result to the ipa host

PORTER

docker inspect schibstedchallenge/dockermaze-porter

Related information

"Entrypoint": [ "/usr/sbin/named" ]"ExposedPorts": { "53/tcp": {} }

Compared with the information provided by the stout and weisse containers, porter is more like a DNS request.

 

Db.doc kermaze a key in the BIND configuration of the DNS domain name must be stout. py.

IPA

docker inspect schibstedchallenge/dockermaze-ipa

Related information

"Entrypoint": [ "/usr/local/bin/start.bash" ]"ExposedPorts": { "6060/tcp": {} }"Env": [ "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "GOLANG_VERSION=1.5.1", "GOLANG_DOWNLOAD_URL=https://golang.org/dl/go1.5.1.linux-amd64.tar.gz", "GOLANG_DOWNLOAD_SHA1=46eecd290d8803887dec718c691cc243f2175fe0", "GOPATH=/go" ]

Golang clearly points to @ nibble_ds and tries to run the IPA image. An error occurs:

2015/11/22 12:20:20 error: envvar AES_KEY not defined

Try to find more information:

 

We can see that the container uses Prana and Eureka (the Netflix stack project) and runs the IPA golang binary. In this example, the challenge author also provides source code to make it easier.

$ file ipaipa: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped

Analyze the IPA application (ipa. go) source code, we know that it listens to port 6060/tcp, and decodes (base64) and decrypts the received data, using the AES-256 CTR mode, environment variable "AES_KEY" provided key. The result is sent to the "weisse" REST endpoint, base64-encoded data is returned, and then returned to the caller. Fragment:

...SNIP...var AesKey = os.Getenv("AES_KEY")func main() {...SNIP...ln, err := net.Listen("tcp", ":6060")...SNIP...func handleConnection(conn net.Conn) { defer conn.Close()br := bufio.NewReader(conn) line, err := br.ReadString('\n')...SNIP...data, err := base64.StdEncoding.DecodeString(line)...SNIP... decdata, err := decrypt(data, []byte(AesKey))...SNIP...transdata, err := transform(decdata)...SNIP...ret := base64.StdEncoding.EncodeToString([]byte(transdata))fmt.Fprintln(conn, ret)}...SNIP...func transform(data []byte) (transdata []byte, err error) { c := goprana.NewClient(goprana.DefaultPort) resp, err := c.Post("weisse", "/turing", "application/octet-stream", bytes.NewReader(data)) if err != nil { return nil, err } defer resp.Body.Close()return ioutil.ReadAll(resp.Body)}

Clue sorting

1. stout uses the post http Method to receive data from its REST endpoint and listens to port 31337/tcp. Get the key (DNS request) from the porter, convert the received data, and send its Base64 encoding to IPA. 2. IPA receives the data sent by stout, decodes (Base64), decrypts the data using the AES_KEY provided by the environment variable, and sends the decrypted content to the weisse endpoint 3. the weisse endpoint applies an Enigma decryption to receive data, requests the porter DNS server through the DNS, and then reports the decryption data to IPA. 4. IPA responds to bas64 encoding and returns to stout5.stout to decrypt bas64. The response is provided to the caller.

We have the ct1.bin file and the FollowTheRabbit key, and make a bold assumption:

AES_KEY is: FollowTheRabbitct1.bin is the encrypted data we want to use the container chain to decrypt.

To better communicate with each other, we need to connect to the container and use Prana & Eureka to communicate directly with weisse and ipa.

 

As a result, we did the following:

docker pull netflixoss/eureka:1.1.147docker run -d --name eureka netflixoss/eureka:1.1.147docker run -d -P --name porter schibstedchallenge/dockermaze-porterdocker run -d -P --name weisse --link porter:porter --link eureka:eureka schibstedchallenge/dockermaze-weissedocker run -d -P -e "AES_KEY=FollowTheWhiteRabbit" --name ipa --link weisse:weisse --link eureka:eureka schibstedchallenge/dockermaze-ipadocker run -d -p 31337:31337 --name stout --link ipa:ipa --link porter:porter schibstedchallenge/dockermaze-stout

I waited a few minutes before the request was sent to the stout endpoint (following the suggestions in the start. bash file)

curl -v -X POST --data-binary @ct1.bin http://localhost:31337/gate --header "Content-Type:application/octet-stream"

 

OK. The direction is correct. Unfortunately, I have never participated in the Dockercon competition before. Fortunately, @ nibble_ds sent me the key of their booth Schibsted.

 

A ruby code snippet is displayed in the scan QR code:

puts 'z4LufsdfTf{bNsfldpE'.bytes.map { |ch| (ch.ord - 1).chr }.reverse.join

After execution, you can get a new key (DockerMazeSecretK3y ). When I try the curl command again to modify AES_KEY AND THE ct1.bin file does not respond to us, how can we get new messages? I still remember that the DockerMaze escape command received an ip parameter, so I made a DNAT ing to stout (PublicIP: 31337-> PrivateIP: 31337), and execute:

escape x.x.x.x

X. x is my public IP address.

Trying to escape... Wait...Hummm… Everything seems to be okay but you must be faster… 20.040787 seconds is too much

To be more efficient, we found that weisse is too slow. The problem lies in this loop:

...SNIP...BFBASE = 'aaa'...SNIP...def get_rotors(nameserver) rotors = []Resolv::DNS.open({:nameserver=>[nameserver]}) do |r| ctr = 0loop do begin n = r.getresource("walzen-#{ctr}.dockermaze", Resolv::DNS::Resource::IN::TXT).data.to_i rescue Resolv::ResolvError break endbf = BFBASE.dup found_chunks = 0 rotors[ctr] = ''while found_chunks < n begin ck = r.getresource("walzen-#{ctr}-#{bf}.dockermaze", Resolv::DNS::Resource::IN::TXT).data.delete('"') rotors[ctr] << ck found_chunks += 1 rescue Resolv::ResolvError next ensure bf.next! end endctr += 1 end endrotorsend

Most request DNS entries are like this:

walzen-0 IN TXT "4"walzen-0-aaa IN TXT "7b57e0a216b65a40534e4c8bcc787a8e5b3722657dcfb0d199950688ef0c718cbf1094bd0ff7d687c69cfba09d42caaa13d4cdb24f8f892877b4a91f596b2615"walzen-0-aab IN TXT "6f48936c561d66625e31702143c2978ddaf19f60dcfd340e3b3c2b725404a820613ad369ae0a30a5b76de14d08d041337c02ceacbed5e7c3deee67ad7f63f529"walzen-0-aac IN TXT "f3523e2746b1e524a48451ff1e5c92f6d796b9b89036c43d8ae8f486c7c1bc2ea601499e6eab81e383c0392c2d0514f0e9324af985507efa116a743523cb00fe"walzen-0-aad IN TXT "1a68df6455c8ec914476fcc5808279f298ed3f5dbba7a3b54b250309d92f17a112b307db75eb1c2af8dd38e473d819afd2e2ea1be6c90b589aba5f470d18459b"walzen-1 IN TXT "4"walzen-1-aaa IN TXT "0ec8580062742e72c3d96fc76d4f21bacdf03887256bb7c9d42a27c5cb43e216405163e7a3427a071033ea3944899f88d63f83e41d91ad1a19b39c455c041294"walzen-1-aab IN TXT "ac8ccfafe184de033afdf13ddcdfd27b8b86989e82d1ffbca99290fbc4a115c2eba05323be80060a30eeaed3689385148aa56e37a6bb4a1bef0db5bd34dbf846"walzen-1-aac IN TXT "eddd05480c7df9c6d0b69b591eb48f7f20175022f4577170ab7ea78e77b04c5d4e029dbf47fa3e8d49e3d83b4b816999ecb178f561081c292f2b6097544136f7"walzen-1-aad IN TXT "18a46635e9b9f6d756753cf35f65e0aac1266c7c5ba25231e5e60bce0f2cb82432da675a09132d5e9acafc76a8110155b2c04d1ff2d5fe73

However, some are not consecutive, resulting in many unnecessary DNS request errors:

walzen-9 IN TXT "4"walzen-9-aaa IN TXT "3a8a13373496029d73b8d44e23147e947f45d5fd8640073f2ff7953858bb5ce076cfbef68860d8986a7a8fc8ad26d9d3f8fc9fee0e56ed65b14cb0fe84acc724"walzen-9-aab IN TXT "299cda0f3001505a3caf0b99e2c380f3b532161aa861b2f00675dfa4d08da0ea550dcc53f581692a5bd6d119744272fac0b7db8c6210ffbc8bc9a166bacd9305"walzen-9-aac IN TXT "a76f638943aae11d925d680948e4672dd252ab54495fc2caf27cf45133b65e7d6ba6820a1225398e214b274dbd00596efbefe6229e18473b20c56de3c135153d"walzen-9-rzd IN TXT "644fe5f18583ebf9c62e1e1f7bc4ecdd44b4ce70a5086c4ad7579a17a9413e3171bfde11790c877704a2a3e8b32891369bb946e9ae2b2c1b

Edit the db.doc kermaze configuration file to keep them all continuous and update the docker image:

docker cp ./modified-db.dockermaze porter:/etc/bind/db.dockermazedocker commit porter redsadic/dockermaze-porter:v2docker run -d -P --name porter redsadic/dockermaze-porter:v2

Run the escape x. x command again:

Trying to escape... Wait...You put the key in the lock and... the door opens! Congratulations! You are out of the labyrinth! Send an email with the following info to big.ideas+DockerMaze@schibsted.com:- IP used to escape- The token 'XXXXXXXXXXXXXXXXXX'- Short explanation about how you escaped

Perfect solution!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.