Thehackerscrew / dCTF

Writeups for dCTF 2021

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

dCTF

1. MISC

1.1 Encrypted Flag I Have

encrypted_flag_i_have

First off we start with this. It looks like yoda-ism, lets take a look at the file. encrypted_flag_i_have

Indeed I was correct. It's Star Wars language called Aurebesh. We can use a decoder from dcode.fr. It's missing {} letter's but we can add them to the flag ourself. Once decoded the flag is: DCTF{MASTERCODEBREAKER}

1.2 Dragon

dragon

Let's check the file dragon_chall

We use Aperisolve to analyze the file and we got the flag from the blue spectrum dragon_chall_solve

1.3 Don't Let it run

dont_let_it_run

At first when opening the PDF there's just a picture of the same dragon as in previous challege so I decided to run strings on the file

strings -n7 dragon.pdf

Right off the bat we can see there's javascript that looks like it's HEX encoded.

Let's grab that string and and run a command.

$ echo 766172205F3078346163393D5B2736363361435968594B272C273971776147474F272C276C6F67272C273150744366746D272C27313036387552596D7154272C27646374667B7064665F316E6A33637433647D272C273736383537376A6868736272272C2737313733343268417A4F4F51272C27373232353133504158436268272C2738333339383950514B697469272C27313434373836335256636E546F272C2731323533353356746B585547275D3B2866756E6374696F6E285F30783362316636622C5F3078316164386237297B766172205F30783536366565323D5F3078353334373B7768696C652821215B5D297B7472797B766172205F30783237353061353D7061727365496E74285F307835363665653228307831366529292B2D7061727365496E74285F307835363665653228307831366429292B7061727365496E74285F307835363665653228307831366329292B2D7061727365496E74285F307835363665653228307831373329292A2D7061727365496E74285F307835363665653228307831373129292B7061727365496E74285F307835363665653228307831373229292A2D7061727365496E74285F307835363665653228307831366129292B7061727365496E74285F307835363665653228307831366629292A7061727365496E74285F307835363665653228307831373529292B2D7061727365496E74285F307835363665653228307831373029293B6966285F30783237353061353D3D3D5F307831616438623729627265616B3B656C7365205F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D6361746368285F3078353736346134297B5F30783362316636625B2770757368275D285F30783362316636625B277368696674275D2829293B7D7D7D285F3078346163392C3078386439376629293B66756E6374696F6E205F30786128297B766172205F30783363366432303D5F3078353334373B636F6E736F6C655B5F3078336336643230283078313734295D285F307833633664323028307831366229293B7D76617220613D27626B706F646E746A636F7073796D6C78656977686F6E7374796B787372707A79272C623D2765787262737071717573746E7A717269756C697A70656565787771736F666D77273B5F30786228612C62293B66756E6374696F6E205F307835333437285F30783337646533352C5F3078313961633236297B5F30783337646533353D5F30783337646533352D30783136613B766172205F30783461633965613D5F3078346163395B5F30783337646533355D3B72657475726E205F30783461633965613B7D66756E6374696F6E205F307862285F30783339623365652C5F3078666165353433297B766172205F30783235393932333D5F30783339623365652B5F30786661653534333B5F30786128293B7D0A |unhex
var _0x4ac9=['663aCYhYK','9qwaGGO','log','1PtCftm','1068uRYmqT','dctf{pdf_1nj3ct3d}','768577jhhsbr','717342hAzOOQ','722513PAXCbh','833989PQKiti','1447863RVcnTo','125353VtkXUG'];(function(_0x3b1f6b,_0x1ad8b7){var _0x566ee2=_0x5347;while(!![]){try{var _0x2750a5=parseInt(_0x566ee2(0x16e))+-parseInt(_0x566ee2(0x16d))+parseInt(_0x566ee2(0x16c))+-parseInt(_0x566ee2(0x173))*-parseInt(_0x566ee2(0x171))+parseInt(_0x566ee2(0x172))*-parseInt(_0x566ee2(0x16a))+parseInt(_0x566ee2(0x16f))*parseInt(_0x566ee2(0x175))+-parseInt(_0x566ee2(0x170));if(_0x2750a5===_0x1ad8b7)break;else _0x3b1f6b['push'](_0x3b1f6b['shift']());}catch(_0x5764a4){_0x3b1f6b['push'](_0x3b1f6b['shift']());}}}(_0x4ac9,0x8d97f));function _0xa(){var _0x3c6d20=_0x5347;console[_0x3c6d20(0x174)](_0x3c6d20(0x16b));}var a='bkpodntjcopsymlxeiwhonstykxsrpzy',b='exrbspqqustnzqriulizpeeexwqsofmw';_0xb(a,b);function _0x5347(_0x37de35,_0x19ac26){_0x37de35=_0x37de35-0x16a;var _0x4ac9ea=_0x4ac9[_0x37de35];return _0x4ac9ea;}function _0xb(_0x39b3ee,_0xfae543){var _0x259923=_0x39b3ee+_0xfae543;_0xa();}

There we go, we go it unhexed and flag is looking right at us!

1.4 Hidden Message

hidden_message

Let's download the file and check it out. After zooming around the file I couldn't find anything unusual so I decided to run zsteg -a fri.png to check if there's anything hidden in the bits

$ zsteg -a images/fri.png
b1,rgb,lsb,xy       .. text: "dctf{sTeg0noGr4Phy_101}"
b3,g,lsb,xy         .. text: "I@4I)$Xl"

The very first line revealed us the flag!

1.5 Leaked spin

leaked_spin

Let's check the hint first before even trying to figure out anything.

leaked_spin_hint

So it's a kind of OSINT challenge, alright I'm confident I can do this. First I google dragonsecsi github and I go in there. We can find that there's repository called DCTF1-chall-leak-spin so let's jump in there and check it out. There's a file called challenge.yml and it contains the flag:

name: "Leak Spin"
author: "Miha M."
category: Web

description: We have confident insider report that one of the flags was leaked online. Can you find it?
value: 100
type: standard

flags:
  - dctf{I_L1k3_L1evaAn_P0lkk4}

tags:
  - web

state: visible
  
version: "1.0"

1.6 Extraterrestial Communication

extraterrestial_communication

Let's check out the hint first.

extraterrestial_communication_hint

Hint provides us with enough information. The first images we're transmitted with SSTV a.k.a Slow Scan Television.

We can use RX-SSTV for this. With RX option Scottie 1 we can decode the signal into a picture. Play the mp3 file and let the RX-SSTV do it's magic.

extraterrestial_communication_solve

Flag: dctf{wHat_ev3n_1s_SSTV}

1.7 Powerpoint Programming

powerpoint

Let's open the file in powerpoint. We see 3 slides one with keypad and submit button, one with Correct text and one with Wrong text. Let's check out the Animations tab, from here we can see that some of the key's have a little lightning symbol beside them which means they're being used in an animation. Click on the Animation pane and a sidebar opens up with the animations. Scroll down and you can see full animation list. Just follow the animation instructions in the sidebar and read the flag: dctf{ppt_1snt_v3ry_s3cur3_1s_1t}

1.8 Show us your ID

show_us_id

Let's check the hint first

show_us_your_id

Let's open the file with strings.

%PDF-1.3
1 0 obj
/Pages 2 0 R
/Type /Catalog
2 0 obj
/Type /Pages
/Kids [ 3 0 R ]
/Count 1
3 0 obj
/Type /Page
/Parent 2 0 R
/Resources <<
/XObject << /Im0 8 0 R >>
/ProcSet 6 0 R >>
/MediaBox [0 0 613 344]
/CropBox [0 0 613 344]
/Contents 4 0 R
/Thumb 11 0 R
4 0 obj
/Length 5 0 R
613 0 0 344 0 0 cm
/Im0 Do
endstream
5 0 obj
6 0 obj
[ /PDF /Text /ImageC ]
7 0 obj
8 0 obj
/Type /XObject
/Subtype /Image
/Name /Im0
/Filter [ /DCTDecode ]
/Width 613
/Height 344
/ColorSpace 10 0 R
/BitsPerComponent 8
/Length 9 0 R
.Photoshop 3.0
printOutput
PstSbool
Inteenum
printSixteenBitbool
printerNameTEXT
printProofSetupObjc
proofSetup
Bltnenum
builtinProof
        proofCMYK
printOutputOptions
Cptnbool
Clbrbool
RgsMbool
CrnCbool
CntCbool
Lblsbool
Ngtvbool
EmlDbool
Intrbool
BckgObjc
Rd  doub@o
Grn doub@o

A whole bunch of stuff.. that doesnt help much. The challenge name gave an idea. Let's grep the file.

$ grep -a -o "id=.*" nyan.pdf
id="646374667b3362306261347d"?>
...

Still a whole lot of stufff but only one thing we're interested in id="646374667b3362306261347d". That looks like a hex encoded string. Lets use unhex on it.

$ echo 646374667b3362306261347d |unhex
dctf{3b0ba4}

We got the flag!

1.9 Company Leak

company_leak

We get this zipfile so lets unzip it unzip leaked.zip. We got two new files. README.md and super_secret.zip

We tried using John the ripper to crack the hash with no success. We tried using CRC32 cracker for it without success. Then after some googling we found about bkcrack which was the correct program to get it cracked. Here's how it happened.

$ bkcrack -C super_secret.zip -c README.md -P README.zip -p README.md
bkcrack 1.2.2 - 2021-05-17
[20:00:18] Z reduction using 285 bytes of known plaintext
100.0 % (285 / 285)
[20:00:19] Attack on 27016 Z values at index 7
Keys: a33fbdc6 5b49420e 6589766e
66.9 % (18073 / 27016)
[20:00:23] Keys
a33fbdc6 5b49420e 6589766e

$ bkcrack -C super_secret.zip -k a33fbdc6 5b49420e 6589766e -U not_so_secret.zip password
bkcrack 1.2.2 - 2021-05-17
[20:04:46] Writing unlocked archive not_so_secret.zip with password "password"
100.0 % (2 / 2)
Wrote unlocked archive.

$ unzip not_so_secret.zip
Archive:  not_so_secret.zip
[not_so_secret.zip] README.md password:
replace README.md? [y]es, [n]o, [A]ll, [N]one, [r]ename: r
new name: not_so_secret_README.md
  inflating: not_so_secret_README.md
  inflating: top_secret.txt

£ cat top_secret.txt
I'd just like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.

dctf{wew_lad_y0u_aCtually_d1d_it}

Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called "Linux", and many of its users are not aware that it is basically the GNU system, developed by the GNU Project.

There really is a Linux, and these people are using it, but it is just a part of the system they use. Linux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called "Linux" distributions are really distributions of GNU/Linux.

(text above is a joke)

2. Crypto

2.1 Forgotten Secret

dockerimage

Lets check the hint.

dockerimagehint

So we have a the image file which you can just unzip to reveal it's secrets inside. So lets do that.

$ 7z x image

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,36 CPUs Intel(R) Core(TM) i9-9980XE CPU @ 3.00GHz (50654),ASM,AES-NI)

Scanning the drive for archives:
1 file, 5911040 bytes (5773 KiB)

Extracting archive: image
--
Path = image
Type = tar
Physical Size = 5911040
Headers Size = 16896
Code Page = UTF-8

Everything is Ok

Folders: 7
Files: 24
Size:       5889333
Compressed: 5911040

$ ls -la
drwxr-xr-x root root   4 KB Mon May 17 23:13:14 2021  .
drwxrwxrwx root root   4 KB Mon May 17 22:30:08 2021  ..
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  59c05f2c50d0d56f7c4cd484794139ac75ac1f21ddd9db596af10844635a04c7
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  67fe39750473214dd953190a3f78507ff653353d49af33494f493dce7431e498
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  853eaa6658b9e21b1373def07ee4f898da46a251f58e1a8a46337ba938f3755c
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  98c9cc069e93e9cae98bbe8369566eda72b487e9ca60509e89d9d7ee98071f0d
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  b15241aee72134a1eda317850258161406ae470b813c26a1a244f7645b553014
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  df6e2b0dba838bcc158171c209ae2c7b8aeec4a8638a2fa981abda520233a170
drwxr-xr-x root root   4 KB Fri May  7 10:38:26 2021  ee6ac2faa564229d89130079d3c24dcb016b6818c2a8f3901ad2a7de1fdb0faf
.rw-r--r-- root root 3.4 KB Fri May  7 10:38:26 2021  7dabd7d32d701c6380d8e9f053d83d050569b063fbcf7ebc65e69404bed867a5.json
.rw-r--r-- root root 5.6 MB Fri May 14 18:17:20 2021  image
.rw-r--r-- root root 674 B  Fri May 14 18:17:20 2021  manifest.json
.rw-r--r-- root root  99 B  Fri May 14 18:17:20 2021  repositories

Lets check out the json file first.

{
  "architecture": "amd64",
  "config": {
    "Hostname": "",
    "Domainname": "",
    "User": "alice",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      "SECRET_KEY=58703273357638792F423F4528482B4D6251655468566D597133743677397A24"
    ],
    "Cmd": [
      "cat",
      "/home/alice/cipher.bin"
    ],
    "Image": "sha256:c57f64aa4be71ead89638dd084c259dad21322dec97f20f12640c4803979ed48",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": null
  },
  "container": "430de829e1ba6b76d0b449dae382eb2bca97366266d2e2d896f0a033a1e7b40c",
  "container_config": {
    "Hostname": "430de829e1ba",
    "Domainname": "",
    "User": "alice",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
      "SECRET_KEY=58703273357638792F423F4528482B4D6251655468566D597133743677397A24"
    ],
    "Cmd": [
      "/bin/sh",
      "-c",
      "#(nop) ",
      "CMD [\"cat\" \"/home/alice/cipher.bin\"]"
    ],
    "Image": "sha256:c57f64aa4be71ead89638dd084c259dad21322dec97f20f12640c4803979ed48",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": {}
  },
  "created": "2021-05-07T07:38:26.332455966Z",
  "docker_version": "20.10.5",
  "history": [
    {
      "created": "2021-02-24T20:20:03.472860777Z",
      "created_by": "/bin/sh -c #(nop) ADD file:0dbb1cd66f708f54f7e6663eabf24095fcd53747bfb09912a118a77e737d9617 in / "
    },
    {
      "created": "2021-02-24T20:20:03.645833664Z",
      "created_by": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
      "empty_layer": true
    },
    {
      "created": "2021-05-07T07:38:15.958998137Z",
      "created_by": "/bin/sh -c #(nop) COPY file:10b1d5253a9830ea19b3097c68c9d434b3f757225a404deb1c2fefb6ca5d49eb in /root/.ssh/id_rsa "
    },
    {
      "created": "2021-05-07T07:38:21.479522094Z",
      "created_by": "/bin/sh -c chmod 600 /root/.ssh/id_rsa"
    },
    {
      "created": "2021-05-07T07:38:21.722655712Z",
      "created_by": "/bin/sh -c #(nop)  ARG SECRET_KEY",
      "empty_layer": true
    },
    {
      "created": "2021-05-07T07:38:21.996777247Z",
      "created_by": "/bin/sh -c #(nop)  ENV SECRET_KEY=58703273357638792F423F4528482B4D6251655468566D597133743677397A24",
      "empty_layer": true
    },
    {
      "created": "2021-05-07T07:38:23.392625191Z",
      "created_by": "/bin/sh -c addgroup -S alice && adduser -S alice -G alice"
    },
    {
      "created": "2021-05-07T07:38:23.710811741Z",
      "created_by": "/bin/sh -c #(nop) COPY file:74c685a989b7fe9e41bbc0b8bdebfd5a7fc9a1c64ac2afbf58a482f005eac558 in /home/alice/cipher.bin "
    },
    {
      "created": "2021-05-07T07:38:24.855457313Z",
      "created_by": "/bin/sh -c chown alice:alice /home/alice/cipher.bin"
    },
    {
      "created": "2021-05-07T07:38:25.860302496Z",
      "created_by": "/bin/sh -c chmod 600 /home/alice/cipher.bin"
    },
    {
      "created": "2021-05-07T07:38:26.109756274Z",
      "created_by": "/bin/sh -c #(nop)  USER alice",
      "empty_layer": true
    },
    {
      "created": "2021-05-07T07:38:26.332455966Z",
      "created_by": "/bin/sh -c #(nop)  CMD [\"cat\" \"/home/alice/cipher.bin\"]",
      "empty_layer": true
    }
  ],
  "os": "linux",
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:33e8713114f88c8cb3f60c8a0a4aefe2500823b2fbbae05488b0185ba226caae",
      "sha256:d38e16a3fa56a15a4e64b3fb44aa2adcdc3201b1c0edb84241c769db67d8370f",
      "sha256:e60b30ada1c8521c7865482f3837f6da5ba98813e446034b6339ee91988cd824",
      "sha256:f652323cff13716b27165effc2f64084be82da5715a108e02c35ae336c2be66a",
      "sha256:a8923ad28101cdd08ed7b6f318e702b602b20808dde85f7c61808d68548da10c",
      "sha256:1d4334271f4299b0e04683c8a2658b4c6dd8a813c99b2d6eabace4070b989967",
      "sha256:84606a800cf88cf82678e669637aed8fcf95d88f15c36f99793be1e853972936"
    ]
  }
}

Whoahh! that's a lot of stuff, but we can see that there's id_rsa private key file, SECRET_KEY environment variable and cipher.bin encrypted file. So we probably need to use that id_rsa to decrypt that cipher.bin and we can assume SECRET_KEY is being used as a passphrase for the privatekey. So first things first, lets find where the privatekey is.

$ tar -tvf layer.tar
drwx------ 0/0               0 2021-05-07 10:38 root/
drwxr-xr-x 0/0               0 2021-05-07 10:38 root/.ssh/
-rw------- 0/0            2635 2021-05-07 10:38 root/.ssh/id_rsa

$ tar -xvf layer.tar
root/
root/.ssh/
root/.ssh/id_rsa

$ cat root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBge7WiWi
2R3XsbedLz7zheAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDEGWD7vPEn
jtMhLD7I370N/FuMBtcLSX1oCpwbqwGpOmmVMMtaLQmTq29pSrKax5+jMWvEZUxR+nJ0Hc
gdyxsGPvP722WkfwMH+BOaq3hY8JQBuNqLWeq600N9erjVBk4e3JDQwKfbMTHYsZk8Qioq
omuyrlF+SMGMzH+M5xsC80qTIlucXAW8ix8id+aflGZWKuQHmcS4m04JFhCCjWmuO3ES96
R6oppvDtu1Lm/uQN/deBNMdzRrjiv/rHvN8sP+y7p2W/nfZMcArYJqGtbKlGaUWxZSBsmd
IdHWa28BS0BrtU0a44bcHuIGhNK2dswhasZSkRqYVD5DQMQrsw6fOmV0/GC8QyEK5d45Tq
EIoBVnbxYnFSJT6wdfDlMU3n+KF4nTOWL6AxhvmxAsn49cPn9IZoyCWVuICjQ6DXZQGw74
4rFsazmrT+ZRX3/012a8nqf2k1PSWFFJq+F9D7d3Tdkx2r5SHAYZFH7cg96IdcRCEZoIoE
Iziy1McKZLk9cAAAWAY+HzKDpq12FQ0QDSCXtt4XuqngE2ewe2o1KUFvcVD/nJZPnpf2Nq
ea+s3SIFJ+hM1+Y+UAQTmFuiTa69yM45ZgkPrilKkHi5+nnNr/g70oSRBIsjwptpoDgSCL
XCqBjvNWVm/I4FkekApDb2Z//ERh1xmfFvx3KDv4SQgfUP3q9PLbOziI3wGnjmqRczilS3
6RHpNlJwbsCOuFgUkU7zymeBzU4OGcQ9Ls+Oh1X3xaAUPSnOqhoIZRiVrS48d38o6Y9j7I
zyeiVKfTRJ7yCPmJf3KrB9u8j0DGlwzkep4OIGH/iQPHALFjfnkBYPEnC0gqRSS+y1/T2z
aXGV1lsE3bh0fuk8Cja2XrB/RZftmBfHFT2YsKGeVlOlfUX0DlxyY06v3Zu8cS2/1QaHu7
KMc2MfUc1WiDe6IzPM+/Pq5aS7IVztzMuIvNMCc2GPF2D3DtQnzmsVbddWa7OhQklhFrQo
OXFEe0mgwUcl78FlLo0xmuOL2mrIfqWEcJ9GjY5ZiqKxWhfdXvXAyXlPzOFRJwX3k9jV5G
0t2ZYVoFdovYL1bDxpOEqANLhxIFaYB/PbJQwiDWxNYrGyx2mbCcrMaikV1taBIjSy8qfL
MPcRBqc2BUsdB8U33q7Ydj5V2nfm+3gUCH655KoQHMq9vOqmn/MciYtNbg9fDwYB4O38uD
3iVSd3nEq4/TbNUlY1PoatM92Okkh0H/0CVirh6WIvrUu5dNnrj7ebY+QgQkPgKLyQYdNd
HHEZi9RVBRHYbYbjJ4CRCBmAB1SCSS42roANni1O4k0sVNn5J66B0bOSktR7MFNJEV9C9y
jVEPXQQD4CGDmRJZH+cnlVaJffw17ZkZZWSVeXfhnZOtus9V9gNLAE6M876AValfW6xKH9
Iu03T5ihMHXbuSzPZG8Nc6WKq2+UGxh5S7UCAixbJQYzuC6A5HVrn9dcDlDbkYpY/DY5GZ
BNrnUIJ1srWk0I10GbVUT+vrxGfa2UdEjZL8EWvsrFd0fKGClK0MYuTLwCAtrh0gerB7Rn
YUh70cCCW5GR8XG8+VBqhyKq0SjaH5ppzTdPcAAkpHheKDkhXoWbP2Spi2HXl6/W6pro8l
BoeZEAmEf2x+X4A+9Rg1Y74YkkjLNVbTfpuJRLwP0vR8ZOawispqmR7roirz6VnT7veeqo
0ai8Ae5bTpinu75vTSteTjSeoKZmZEx5GhH+nFdL0CFwXoczPRRe7X7WjbrNxNs7EkYqD0
HuI6QdWH/A46s768uqSoi0WpTU5q2eaE7U94xef4ndAMWUYeS4mVZYqiZsoJ85jgdGi56C
ctdImSVihnmgS3NPgPxO1YQvueEZgCMNCYdf314FtXWXfGXP45c3CFZPq9KHdt+sWC0hDb
+77SGlOIJOiVhguKwiw+WORRC87W7mVO1tUrK9JrPcXYMxV3Wpis6WKyYu2yWTvd2BAWRW
djHRj+bdAYBSCuRCsoGkDt3BNO4+BTo/AcdGJ3QnJMB+qoUcaeg+LvUwv4jDhmRl5Mj4UE
OphgXY+3oFbsyGrH4ArpZhp9KWbVY/kjl7juWOn14IiScVMSi+yiG5PkIaHfp9LRrGGy1e
hyOv30gu2NYb30nwTrYo2jNFY2Txq9Ga7cMxWqpuwkSMUBsRx5LHMoZq0knjC4ghhY/Wbf
OuxqRaGsGHe+J8kMOvbE7sdsLRGZgaxyWIGSiXozU0oqQWYYJcQ40jwscUHzJHli/G+U2R
cwQpIlX4u8zjJzRSc7RxVe4/YxeEbPtdJCCi0gJCqLdpY/adJwgAm7fCaB3YZndD/bJBdj
5mqRQPpED15FITyJ5LweORkItB+/KfsIHHCRtITVEH065o+aaCbIn17q3ToIExJujPVtTw
vuYZEA==
-----END OPENSSH PRIVATE KEY-----

Well well well, look at that. It's old format and definitely not one used encrypt the cipher since no tools can encrypt files with that format. So we must change the key's format into something more modern like PEM. We can do it like this:

puttygen id_rsa -O private-openssh -o dctf.pem and we use the SECRET_KEY as password. After that we can use cyberchef to decrypt the cipher.bin with the new privatekey and password.

Vóila we got the flag Do not hardcode keys inside images! But here you go dctf{k33p_y0r_k3ys_s4f3}.

2.2 Strong Password

strong_password

Let's check the hint!

strong_hint

Alright so the hint tells us not to use fcrackzip which is probably the most used zip cracker. But good thing we know how to run John the ripper.

Lets do this!

$ zip2john strong_password.zip > strong.hash
ver 2.0 efh 9901 strong_password.zip/lorem_ipsum.txt PKZIP Encr: cmplen=5171, decmplen=17174, crc=CEFA3672

$ john --wordlist=/usr/share/wordlists/rockyou.txt strong.hash
Using default input encoding: UTF-8
Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 512/512 AVX512BW 16x])
No password hashes left to crack (see FAQ)

$ john --show strong.hash
strong_password.zip/lorem_ipsum.txt:Bo38AkRcE600X8DbK3600:lorem_ipsum.txt:strong_password.zip:strong_password.zip

1 password hash cracked, 0 left

$ unzip strong_password.zip
Archive:  strong_password.zip
   skipping: lorem_ipsum.txt         unsupported compression method 99

$ 7z x strong_password.zip -pBo38AkRcE600X8DbK3600

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,36 CPUs Intel(R) Core(TM) i9-9980XE CPU @ 3.00GHz (50654),ASM,AES-NI)

Scanning the drive for archives:
1 file, 5373 bytes (6 KiB)

Extracting archive: strong_password.zip
--
Path = strong_password.zip
Type = zip
Physical Size = 5373

Everything is Ok

Size:       17174
Compressed: 5373

$ grep -o dctf.* lorem_ipsum.txt
dctf{r0cKyoU_f0r_tHe_w1n} Etiam in volutpat nunc. Aliquam erat volutpat. Ut dapibus, sem at posuere sollicitudin, tellus elit faucibus ligula, ut malesuada leo erat eu sem. Nam nulla lacus, feugiat placerat porttitor eu, sodales quis quam. Duis efficitur, nisl ege

We got the flag: dctf{r0cKyoU_f0r_tHe_w1n}

2.3 Julius' ancient script

julius

We get a file with one line in it rq7t{7vH_rFH_vI6_pHH1_qI67}. Looks like a basic caesar shift cipher but there's numbers involved. Because there are number we can use this online tool Paste the line there and press calculate. Result is in ROT22 dctf{th3_d13_h4s_b33n_c4st}

2.4 Just Take Your Time

takeyourtime

Let's check the hint.

takeyourtimehint

Alrighty then, let's open up the file and see what we're working with.

#!/usr/bin python3

from flag import flag
from Crypto.Cipher import DES3
from time import time
from random import randint
from secrets import token_hex
from pytimedinput import timedInput

guess = 3
TIMEOUT = 1

a = randint(1000000000000000, 9999999999999999)
b = randint(1000000000000000, 9999999999999999)

print("Show me you are worthy and solve for x! You have one second.")
print("{} * {} = ".format(a, b))

answ, _ = timedInput("> ", timeOut = 1, forcedTimeout = True)

try:
    assert(a*b == int(answ))
except:
    print("You are not worthy!")
    exit(1)

key = str(int(time())).zfill(16).encode("utf-8")
secret = token_hex(16)
cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
encrypted = cipher.encrypt(secret.encode("utf-8"))
print("You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!")
print(encrypted.hex())

start_time = time()
while(time() - start_time < TIMEOUT and guess > 0):
    delta = time() - start_time
    answ, _ = timedInput("> ", timeOut = TIMEOUT + 1 - delta, forcedTimeout = True)

    try:
        assert(secret == answ)
        break
    except:
        if answ != "":
            guess -= 1
            if (guess != 1):
                print("You are wrong. {} guesses remain.".format(guess))
            else:
                print("You are wrong. {} guess remains.".format(guess))

if (secret != answ):
    print("You have been unsuccessful in your quest for the flag.")
else:
    print("Congratulations! Here is your flag.")
    print(flag)

So it seems like our first stage is about supplying a product of two numbers. Numbers are between 1000000000000000 and 9999999999999999 shouldn't be too hard since we have python. So we device this:

from pwn import *

p = remote('dctf-chall-just-take-your-time.westeurope.azurecontainer.io', 9999)
p.recvline()
tmp = p.recvline().strip().decode()
info(tmp)
tmp = tmp.split(' ')
res = int(tmp[0])*int(tmp[2])
success(str(res))
p.sendlineafter("> ",str(res))
p.interactive()

And we try it out and we get this as a result:

[*] Switching to interactive mode
You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!
21e96ee7d28cb08004001afafb7d7a592336b18d2c5b5b9704838d76107235fa
> You have been unsuccessful in your quest for the flag.
[*] Got EOF while reading in interactive

Alright time to look at the source code again.

key = str(int(time())).zfill(16).encode("utf-8")
secret = token_hex(16)
cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
encrypted = cipher.encrypt(secret.encode("utf-8"))
print("You have proven yourself to be capable of taking on the final task. Decrypt this and the flag shall be yours!")
print(encrypted.hex())

start_time = time()
while(time() - start_time < TIMEOUT and guess > 0):
    delta = time() - start_time
    answ, _ = timedInput("> ", timeOut = TIMEOUT + 1 - delta, forcedTimeout = True)

So we're dealing with Triple DES encryption which is using current time as a key but also filling it with zero's to deal with the key requirement length. It's using CFB mode which is a type of Stream cipher and its using b'00000000' as IV.

We need to findout what that secret is and since token_hex(16) is always random we can't predict that so we have to decrypt the hash we got from the server. We can do it like this:

t = int(time())
p.recvline()
encrypted = str(p.recvline().decode())
info(encrypted)
# t-1 is just my setting
# You could have done it with for loop like: for i in range(t, t+3) if you wanted to try more than one time
key = str(t-1).zfill(16).encode()
cipher = DES3.new(key, DES3.MODE_CFB, b"00000000")
FUZZ = bytes.fromhex(encrypted)
decrypted = cipher.decrypt(FUZZ)
success(decrypted)
p.sendlineafter("> ", decrypted)
opt = p.recv(1024)
print(opt.decode())

After trying it out we get this:

[*] 207aafe5730ee93c28255f9593eed8653b494ab65611cb51f7024d8942a4851b
[*] 2f1b2b8d62716603a9c9f90f0ca72501
Congratulations! Here is your flag.
dctf{1t_0n1y_t0Ok_2_d4y5...}

2.5 Scooby-doo

scooby-doo

The hint that is shown here came in the last day since it had so very little solves.

scooby-doo-hint

The file contains 10000 lines which are all encrypted by what we can guess Enigma cipher.

Bruteforcing Enigma felt like a gross idea so we figured to try something else and we came up with this:

#!/bin/bash

a="ABCDEFGHIJKLMNOPQRSTUVWXYZ"

for i in {1..22}
do
s=$(cat cipher.txt | cut -b $j | tr -d '\n')
  for x in {0..25}
  do
      cc=${a:$x:1}
      if [[ $s == *"$c"* ]]
      then
          printf ''
      else
          printf ${a:$x:1}
      fi
   done
done

It compares every character by character by line and if it matches the position in the alphabet it prints it out. After running the script for ~1sec we get the flag. (Didn't bother to write about the hours of mistakes on this.)

DCTF{TURINGWOULDBEPROUD}

2.6 Private Encryption Mistake

pem

Check the hint

pemhint

Alrighty then lets take a look at the actual file we got.

blurred

Well this looks awefully familiar. There's a blogpost by cryptohack that tells it's possible to create a fully functional privatekey from a poorly blurred one. After reading the blogpost this was a walk-in-the-park. Let's fire up our OCR tool and extract the data from the image.

import pytesseract

try:
    import Image, ImageOps, ImageEnhance, imread
except ImportError:
    from PIL import Image, ImageOps, ImageEnhance

pytesseract.pytesseract.tesseract_cmd = 'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'

decode = pytesseract.image_to_string(r'C:\\Users\\hackernrone\\Downloads\\blurred.png')
print(decode)

A little messy output like always with these OCR tools, we need to go through it by hand and this is what we get as a result.


MIIJKQIBAAKCAgEAupQ7hhy0AQR0LRMZgP/Kl6J3l2+U+wp1YyVB8oDYvslE3AXU
3igwX2LOYgG/JIHQ5UI2G/0Fu5iPPikh3JoUABGFyPwWsBLnohdBBtpvfRLprhbB
lsKwjZfLJIrRex+sSFkcT9zVs1VH4JfcJAbeBNK/aQdMqc1i4JQ1xsQny4ZH7TZe
CXBigK99+V05C+ENRS1uWi9ixgcbMWCCBHsTq0Kl5FIfPvVJVBr075bf7DdARSRU
Wx/FtKVMlWe/nGUTz/ezu2jOx69kd+hvtzX1JVkeY+AFi7Ldta2tNaH/8kitzoXK
JC+6A+LQXynmjQdH9RGsg7QygFjPvIcgwE8LHsMt62OKcIx5LMHlW4lvLK/EZMnr

ZEt6WwyEqHhPyP0CggEBAMplAvElBwRTMaT6FfWwi149Q+C1+ogaRc686CkCEs7p
zWjt4+Tg3cndxj/p2Q3Z1AzJH8h/vfZruAQHF/UFwXIAPmuzS1K0HgnNHxr355vs
AYfArpTJeyZoRttQOXvRhM+c887RWGXX278VVS5e5mh16Dn0rKpDcRnsVMahBhTg
+4XheX0zJRa3lOnoWgRLFGcJj9px4Gk7PkZnx24S2bCb7GUbisvtELkLfAvVcGIS
vvJGbeovAGpArRoaCbpnRL96N50zOWGqHeXJFljvNDvfpVAbykf+50d2VApvElQ3
/v7UHVZEfszMk3g1z+RLpgVmtltCsFvDSkDW9omfoJ0CggEBAIBfu08VPrN+B8iD
QpyO2BBUDei8fjdskpvehjWGDqzKNYDxdVcAdERtk6DSWuzpvwPNbTRm6u3v66yu
QkHn9gBlxX1sYe5P9ExqP2p+Au8hR/8s7bhVa8G53WX1Dl47QVSwbKVOWSWtQSwB
hiB9s1YqgAlhcKBWP6vFbavr3VBYY5ln/018rYvR1euDVTUVZdSMmbq3gScF4fhv
NESMd1Je7XjygbVTPJPi1PcT/SgyDRUwz0RPYIvLlA3qT9ae7s5WTp1fanv5MV6p
4LnekTQ/CVjWSorY7xdXTCMfBK1GF7WhVGG4fVSPX8QeIPKUxKkQXgKAFJrCSjj7
CLG5pPkCggEAflfmKUDTC4kfkXwoXzHxHkgialFPbszvzOmyB39q3E2pU5pFTChv

From that we need to extract the needed values to create the new RSA key. I won't bother explaining too much here so I'll just dump my script and you can figure it out!

from Crypto.Util.number import bytes_to_long, isPrime
from Crypto.PublicKey import RSA
from base64 import b64decode


# Partial private key received from image
upper_key = b"""
MIIJKQIBAAKCAgEAupQ7hhy0AQR0LRMZgP/Kl6J3l2+U+wp1YyVB8oDYvslE3AXU
3igwX2LOYgG/JIHQ5UI2G/0Fu5iPPikh3JoUABGFyPwWsBLnohdBBtpvfRLprhbB
lsKwjZfLJIrRex+sSFkcT9zVs1VH4JfcJAbeBNK/aQdMqc1i4JQ1xsQny4ZH7TZe
CXBigK99+V05C+ENRS1uWi9ixgcbMWCCBHsTq0Kl5FIfPvVJVBr075bf7DdARSRU
Wx/FtKVMlWe/nGUTz/ezu2jOx69kd+hvtzX1JVkeY+AFi7Ldta2tNaH/8kitzoXK
JC+6A+LQXynmjQdH9RGsg7QygFjPvIcgwE8LHsMt62OKcIx5LMHlW4lvLK/EZMnr
"""

lower_key = b"""
ZEt6WwyEqHhPyP0CggEBAMplAvElBwRTMaT6FfWwi149Q+C1+ogaRc686CkCEs7p
zWjt4+Tg3cndxj/p2Q3Z1AzJH8h/vfZruAQHF/UFwXIAPmuzS1K0HgnNHxr355vs
AYfArpTJeyZoRttQOXvRhM+c887RWGXX278VVS5e5mh16Dn0rKpDcRnsVMahBhTg
+4XheX0zJRa3lOnoWgRLFGcJj9px4Gk7PkZnx24S2bCb7GUbisvtELkLfAvVcGIS
vvJGbeovAGpArRoaCbpnRL96N50zOWGqHeXJFljvNDvfpVAbykf+50d2VApvElQ3
/v7UHVZEfszMk3g1z+RLpgVmtltCsFvDSkDW9omfoJ0CggEBAIBfu08VPrN+B8iD
QpyO2BBUDei8fjdskpvehjWGDqzKNYDxdVcAdERtk6DSWuzpvwPNbTRm6u3v66yu
QkHn9gBlxX1sYe5P9ExqP2p+Au8hR/8s7bhVa8G53WX1Dl47QVSwbKVOWSWtQSwB
hiB9s1YqgAlhcKBWP6vFbavr3VBYY5ln/018rYvR1euDVTUVZdSMmbq3gScF4fhv
NESMd1Je7XjygbVTPJPi1PcT/SgyDRUwz0RPYIvLlA3qT9ae7s5WTp1fanv5MV6p
4LnekTQ/CVjWSorY7xdXTCMfBK1GF7WhVGG4fVSPX8QeIPKUxKkQXgKAFJrCSjj7
CLG5pPkCggEAflfmKUDTC4kfkXwoXzHxHkgialFPbszvzOmyB39q3E2pU5pFTChv
"""

def get_values(priv_key):
    results = []
    data = hex(bytes_to_long(b64decode(priv_key)))
    results = data.replace('02820100', ',0x').replace('0282010100', ',0x').replace('0282020100', ',0x').split(',') # should be modified accordingly
    return results

#print ("[*] Upper key values:", get_values(upper_key))
#print ("\n")
#print ("[*] Lower key values:", get_values(lower_key))
#print ("\n")

#N_upper_bits = 0xba943b861cb40104742d131980ffca97a277976f94fb0a75632541f280d8bec944dc05d4de28305f62ce6201bf2481d0e542361bfd05bb988f3e2921dc9a14001185c8fc16b012e7a2174106da6f7d12e9ae16c196c2b08d97cb248ad17b1fac48591c4fdcd5b35547e097dc2406de04d2bf69074ca9cd62e09435c6c427cb8647ed365e09706280af7df95d390be10d452d6e5a2f62c6071b316082047b13ab42a5e4521f3ef549541af4ef96dfec37404524545b1fc5b4a54c9567bf9c6513cff7b3bb68cec7af6477e86fb735f525591e63e0058bb2ddb5adad35a1fff248adce85ca242fba03e2d05f29e68d0747f511ac83b4328058cfbc8720c04f0b1ec32deb638a708c792cc1e55b896f2cafc464c9eb
#p_lower_bits = 0x644b7a5b0c84a8784fc8fd
q = [0xca6502f12507045331a4fa15f5b08b5e3d43e0b5fa881a45cebce8290212cee9cd68ede3e4e0ddc9ddc63fe9d90dd9d40cc91fc87fbdf66bb8040717f505c172003e6bb34b52b41e09cd1f1af7e79bec0187c0ae94c97b266846db50397bd184cf9cf3ced15865d7dbbf15552e5ee66875e839f4acaa437119ec54c6a10614e0fb85e1797d332516b794e9e85a044b1467098fda71e0693b3e4667c76e12d9b09bec651b8acbed10b90b7c0bd5706212bef2466dea2f006a40ad1a1a09ba6744bf7a379d333961aa1de5c91658ef343bdfa5501bca47fee74776540a6f125437fefed41d56447ecccc937835cfe44ba60566b65b42b05bc34a40d6f6899fa09d]
#dp = 0x805fbb4f153eb37e07c883429c8ed810540de8bc7e376c929bde8635860eacca3580f175570074446d93a0d25aece9bf03cd6d3466eaedefebacae4241e7f60065c57d6c61ee4ff44c6a3f6a7e02ef2147ff2cedb8556bc1b9dd65f50e5e3b4154b06ca54e5925ad412c0186207db3562a80096170a0563fabc56dabebdd5058639967ff4d7cad8bd1d5eb8355351565d48c99bab7812705e1f86f34448c77525eed78f281b5533c93e2d4f713fd28320d1530cf444f608bcb940dea4fd69eeece564e9d5f6a7bf9315ea9e0b9de91343f0958d64a8ad8ef17574c231f04ad4617b5a15461b87d548f5fc41e20f294c4a9105e0280149ac24a38fb08b1b9a4f9
#dq_upper_bits = 0x7e57e62940d30b891f917c285f31f11e48226a514f6eccefcce9b2077f6adc4da9539a454c286f
e = 0x10001

def get_p():
    result = []
    dp = int(get_values(lower_key)[2], 16) # just comment this if you add the values inside script ^ like above
    for kp in range(1, e):
        p_mul = dp * e - 1
        if p_mul % kp == 0:
            p = (p_mul // kp) + 1
            if isPrime(p):
                result.append(p)
    return result

def get_n():
    n = [P * Q for P in get_p() for Q in q]
    return n


# Fixed reconstruct function which should be faster
def reconstruct_RSA2(pt, qt, nt):
    # Find all p * q == n combinations and make a dictionary
    combinations = {n: (x, y) for n in nt for x in pt for y in qt if n == x * y}

    if len(combinations) > 1:
        print("\n[*] Printing multiple keys\n\n")
    else:
        print("[*] Final key incoming..\n\n")
    # loop through hashmap where n = p*q combinations. n = n and p_and_q tuple where p and q
    for n, p_and_q in combinations.items():
        p, q = p_and_q[0], p_and_q[1]

        # last RSA calculations before reconstructing the private key
        phi = (p - 1) * (q - 1)
        d = pow(e, -1, phi)
        key = RSA.construct((n, e, d, p, q))
        pem = key.exportKey('PEM')
        print(pem.decode(), "\n\n\n")
        f = open('dctf_id_rsa', 'w')
        f.write(pem.decode())
        f.close

if __name__ == '__main__':
    reconstruct_RSA2(get_p(), q, get_n())
    print ("[*] Creating id_rsa...")
    import os; os.chmod('dctf_id_rsa', 0o600)
    print ("[*] Connecting to target server...")
    import paramiko
    hostname = 'dctf1-chall-private-encryption-mistake.westeurope.azurecontainer.io'
    port = 2222
    user = 'user'
    key = 'dctf_id_rsa'
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(hostname, username=user, key_filename=key, port=port)
    shell = ssh.invoke_shell()
    print(shell.recv(8192).decode())
    print(shell.recv(8192).decode())

That's a stripped down version of a script I used in MidnightSunCTF earlier this year which had the same idea of creating a new key from partial key.

2.7 A Simple SP Box!

spbox

Won't write much about this since I'm not the one in our team who managed to do this so I don't understand it that well.

This challenge is about Substitution Permutation. Looking at the source code.

Our available alphabet is abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!@#$%.'"+:;<=}{ which gets scrambled for 12 rounds int(2 * ceil(log(len(message), 2))) where message being the flag length which we can get by just connecting to the server.

We need to reverse that operation to get the correct characters for the flag, and after that we need to unscramble the flag so its actually readable. You can find solve here or scrolling down.

from pwn import *
from string import ascii_letters, digits
from math import ceil, log

alp = ascii_letters + digits + "_!@#$%.'\"+:;<=}{"

sb = {}

fl = 42
r = int(2 * ceil(log(fl, 2)))

p = remote('dctf1-chall-sp-box.westeurope.azurecontainer.io', 8888)
p.recvline()
ct = p.recvline().strip().decode()
info(ct)

for i in range(len(alp)):
    p.sendlineafter('> ', alp[i] * fl)
    p.recvline()
    sb[p.recvline().decode()[0]] = alp[i]

print(sb)

flag = ''
for c in ct:
    flag += sb[c]

info(flag)

def un_shuffle(m):
    global fl
    res = [''] * fl
    s1 = m[:fl // 2]
    s2 = m[fl // 2:]

    i = 0
    x = 0
    y = 0
    while i < fl:
        if i % 2 == 1:
            res[i] = s1[x]
            x += 1
        else:
            res[i] = s2[y]
            y += 1
        i += 1
    return ''.join(res)

for _ in range(r-1):
    flag = un_shuffle(flag)

success(flag)
p.sendlineafter('> ',flag)
opt = p.recv(1024)
print (opt.decode())

2.8 This one is really basic

basic

The description with "The answer to life, the universe, and everything." movie line from Hitchikers Guide to the Galaxy and the answer to that is 42. Let's check the file

$ less cipher.txt
Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTVjAxV2JETlhhMUpUVmpBeFYySkVUbGhoTVVwVVZtcEJlRll5U2tWVWJHaG9UVlZ3VlZadGNFSmxSbGw1VTJ0V1ZXSkhhRzlVVmxaM1ZsWmFkR05GU214U2JHdzFWVEowVjFaWFNraGhSemxWVm14YU0xWnNXbUZrUjA1R1UyMTRVMkpIZHpGV1ZFb3dWakZhV0ZOcmFHaFNlbXhXVm0xNFlVMHhXbk5YYlVaclVqQTFSMVV5TVRSVk1rcElaSHBHVjFaRmIzZFdha1poVjBaT2NtRkhhRk5sYlhoWFZtMHhORmxWTUhoWGJrNVlZbFZhY2xWcVFURlNNVlY1VFZSU1ZrMXJjRWxhU0hCSFZqRmFSbUl6WkZkaGExcG9WakJhVDJOdFJraGhSazVzWWxob1dGWnRNSGhPUm14V1RVaG9XR0pyTlZsWmJGWmhZMnhXY1ZGVVJsTk5WbFkxVkZaU1UxWnJNWEpqUld4aFUwaENTRlpxUm1GU2JVbDZXa1prYUdFeGNHOVdha0poVkRKT2RGSnJhR2hTYXpWeldXeG9iMWRHV25STlNHaFBVbTE0VjFSVmFHOVhSMHB5VGxac1dtSkdXbWhaTW5oW.............

Looks like base64 so we probably need to decode it 42 times with base64.

$ cat cipher.txt | base64 -d | base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d | base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d| base64 -d
dctf{Th1s_l00ks_4_lot_sm4ll3r_th4n_1t_d1d}

2.9 Data Recovery

datarec

Lets check the hint

datarechint

Alright, lets get on with it. Lets unzip the file we got.

$ unzip recovered_data.zip
Archive:  recovered_data.zip
   creating: content/
  inflating: content/a.out
  inflating: content/accounting.xls
  inflating: content/alarm
  inflating: content/backup.db
  inflating: content/cat.webm
  inflating: content/code.dat
  inflating: content/compiled.exe
  inflating: content/config.cfg
  inflating: content/encrypted
  inflating: content/encrypted1
  inflating: content/fish.bmp
  inflating: content/flag.txt
  inflating: content/hint.txt
  inflating: content/how-to.png
  inflating: content/important.docx
  inflating: content/kernel.bin
  inflating: content/logo.svg
  inflating: content/logs.txt
  inflating: content/macosx.app
  inflating: content/main_base.db
  inflating: content/sauce
  inflating: content/secret.cpt
  inflating: content/secret_code.jpg
  inflating: content/security_route.zip
  inflating: content/source.zip
  inflating: content/statistics.csv

Lets run file command on all of them.

$ file ./*                                                                                                                  130 ⨯
./accounting.xls:     data
./alarm:              JPEG image data, Exif standard: [TIFF image data, big-endian, direntries=0], baseline, precision 8, 639x629, components 3
./a.out:              data
./backup.db:          data
./cat.webm:           WebM
./code.dat:           GIF image data, version 89a, 446 x 526
./compiled.exe:       data
./config.cfg:         JPEG image data, Exif standard: [TIFF image data, big-endian, direntries=0], progressive, precision 8, 1080x885, components 3
./encrypted:          data
./encrypted1:         ASCII text
./fish.bmp:           ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
./flag.txt:           PNG image data, 561 x 789, 8-bit colormap, non-interlaced
./hint.txt:           GIF image data, version 89a, 498 x 440
./how-to.png:         PNG image data, 561 x 789, 8-bit colormap, non-interlaced
./important.docx:     data
./kernel.bin:         data
./logo.svg:           ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
./logs.txt:           ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
./macosx.app:         ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
./main_base.db:       ISO Media, MP4 Base Media v1 [IS0 14496-12:2003]
./sauce:              JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 678x590, components 3
./secret_code.jpg:    ISO Media, MP4 v2 [ISO 14496-14]
./secret.cpt:         ISO Media, MP4 v2 [ISO 14496-14]
./security_route.zip: PNG image data, 128 x 128, 8-bit/color RGBA, non-interlaced
./source.zip:         ISO Media, MP4 v1 [ISO 14496-1:ch13]
./statistics.csv:     RIFF (little-endian) data, Web/P image, VP8 encoding, 640x476, Scaling: [none]x[none], YUV color, decoders should clamp

Look at that there's one ASCII text file. Wonder whats inside of it, lets check it.

$ head -10 encrypted1
OiFpbn5qampiamb8xzjfcvxYvjxqauGAamp4ampqHA8YEzUDBxoFGB4LBB5EHhIe9hcxBbYi/IzR
apdtoEUQI082sVmC9BN7Bj79xxizmPw2D765SvjmpmbhDtpzzrjGr5SdV7VTTUQmwxHa6as+oSNY
cv0Zk6T/NeREPhG9sOCUnaPkIY/Qq+X5g80Hn50UNuzDGDkfGLnbMJZWPPdlOQ/wTbm93MOcFIZA
1cVGtYMW1dfW+pURAQefu1VTMVe+yck7BMB19wOXC9y//bue6xWv9z8fE5vMxJey0pQ6J9RYBzFX
nJS+PcdT76FhcLVWU+mLXZvrgwIt9BmFwfkzQDmHxtfWYi1V3WeXN/M3AVuy1Dfcm8vRQ7ogkCnC
puLh1GB3NCzdd0anr7uf1/PAvCd5V9eZpyjhsT2e2IBakNMFuDo15HwFtgqbX4chylWdx9leHtW6
7ogXUTHxU5rXywAWFVc/X9eWmIgezn13jeQc6Kwdl0l86ddlq7u+ueGU2geBJfEAvfCQGzHHlZO1
NPxbhJc7FcizX7+oXTc3NJYIzYE678kT3lKOE8LmxFdXkLzmiRg/13fHE7bxWgv8nh3ZNo+rUbC4
nqWd7GS7nV442t9FHIZXtBBxlR85m6LdkoEnCdCIqNeDKwDeD971JXzReZqwyXNssbnRkstRsC2w
pwcfHSdNUtKQ+3Xw7Z6oNdYVbi+7kKyWgQWrmBPso9JbuuTrwp1snh4TW3K9Pm2FcUiZtERKBdCz

Looks like another base64 file. But after decoding it we get stuck. Doesn't seem like it is anything after that but it has to be this file according to the hint. Let's use our good friend CyberChef.

Use encrypted1 file as input and select from Base64. After that we figured out to try Magic function with intensive mode, and that was fruitful, the very first result says that the file can be XOR'd into ZIP file. So lets do just that.

After converting it into a ZIP we can use CyberChefs Unzip module to get a look at the files inside.

We get a very_important.txt which has a lot of text inside so we just do simple ctrl+f search and look for dctf and Vóila we got ourselves a flag

dctf{x0r_eNcRypt1on_brUt3foRce}

2.10 Lockpicking

Didn't solve this one during CTF, challenge can be found here and solve can be found in here

3. Web

3.1 DevOps vs SecOps

devopsvssecops

Let's check the hint just in case.

devopshint

Hint gives us information about CI/CD which can be done with github! So let's go find out more from their github. There's a repo called DCTF1-chall-devops-vs-secops, we check it out and we notice there's 2 different branches. Main and devops. There's nothing in the main branch but maybe there's something in the devops. Changing branch and we see a new directory .github/workflows in the workflow directory there's ctfd.yml file

name: CTFD
on:
  push:
    branches: [ devops ]
  pull_request:
    branches: [ devops ]

jobs:
  build:
    runs-on: ubuntu-18.04
    container:
      image: docker.pkg.github.com/dragonsecsi/infra/ghactions:1.0
      credentials:
        username: Aleks-dotcom
        password: ${{ secrets.REGISTRY_TOKEN }}
    steps:
      - name: Checkout repo
        uses: actions/checkout@v2
      - name: Checkout .github
        uses: actions/checkout@v2
        with:
          repository: dragonsecsi/.github
          path: ./data
      - name: ctfd upload challenge
        run: python3 data/setup.py ${{ secrets.ADMIN_TOKEN }}

Here we can see it uses a different repo repository: dragonsecsi/.github so we need to check that out as well since the flag wasn't here.

Going to .github repo we find setup.py file with the flag inside.

import os
import re
import sys

def init():
    #change this:
    CTFD_TOKEN = sys.argv[1]
    #change this:
    CTFD_URL = "https://dctf.dragonsec.si"
    

    os.system(f"echo '{CTFD_URL}\n{CTFD_TOKEN}\ny' | ctf init")

def file():
    for subdirs, dirs, files in os.walk(".."):
        for dirr in dirs:
            if "DCTF-chall" in dirr:
                return dirr

if __name__ == "__main__":
    init()
    print("dctf{H3ll0_fr0m_1T_guy}")
    os.system(f"ctf challenge sync challenge.yml ;ctf challenge install challenge.yml ")

3.2 Simple Web

simple_web

Lets go to the site. On checking the website, it just a basic website with a simple checkbox and submit button. On submitting the form and intercepting the request, you will notice it sends the following data to /flag route.

flag=1&auth=0&Submit=Submit

On changing auth from 0 to 1 and sending the request, you will get the flag.

$ curl "http://dctf1-chall-simple-web.westeurope.azurecontainer.io:8080/flag" --data "flag=1&auth=1&Submit=Submit"
There you go: dctf{w3b_c4n_b3_fun_r1ght?}

3.3 Secure API

On visiting the website, it shows an error message, saying Auth header not found, let's see if we can find an api route which can generate a token for us. On looking around we found login route which accepts POST request.

curl -X POST http://dctf1-chall-secure-api.westeurope.azurecontainer.io:8080/login

On sending the requests, it throws an error saying username and password field missing and looking on the first error message, we can probably guess username and password is guest. Let's send the request.

curl -X POST -d "username=guest&password=guest" http://dctf1-chall-secure-api.westeurope.azurecontainer.io:8080/login
{"Token":"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiZXhwIjoxNjIxNDQwMzk0fQ.E_pRzWEfzyP2FNz4nVr0yEXTrxxSTXAw4nyo1kyMxfY9lHPz8Tk8eyAAAz2ESr6ySPqqKZ72Qy1ZajlROY3ntA"}

Now we got our token, lets send this token to the default api route we found.

curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiZXhwIjoxNjIxNDQwMzk0fQ.E_pRzWEfzyP2FNz4nVr0yEXTrxxSTXAw4nyo1kyMxfY9lHPz8Tk8eyAAAz2ESr6ySPqqKZ72Qy1ZajlROY3ntA" http://dctf1-chall-secure-api.westeurope.azurecontainer.io:8080
{"Message":"Hi, guest! You are not admin, I have no secret for you."

Looks like we need to be admin. Let's decode the json token first. All the juicy info in JWT is usually stored in second index if we conside whole jwt as an array.

echo "eyJ1c2VybmFtZSI6Imd1ZXN0IiwiZXhwIjoxNjIxNDQwMzk0fQ==" | base64 -d
{"username":"guest","exp":1621440394}

Changing guest to admin will get the flag. But we can directy edit it, we need to find the code with which jwt was signed let's use hashcat for decrypting.

hashcat -a0 -m 16500 hash /usr/share/wordlists/rockyou.txt

This gives the password as 147852369, just sign the jwt with this secret and send the request.

curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjIxNDQwMzk0fQ.i-gNZj1T9D4f0_iEnmtYXbp-G58ajb3OtmHNUYfyJPG3uTRrcnoVS080MPRsUjVD6qjS2jm_7Eqf6sDrhJLqaQ" http://dctf1-chall-secure-api.westeurope.azurecontainer.io:8080

{"Message":"Hi, admin! I have a secret for you.","Secret":"dctf{w34k_k3y5_4r3_n0t_0k4y}"}

We got our flag xD.

4. Reverse Engineering

4.1 Bell

The challenge description looked like this:

image

I started the challenge by downloading the file and running the file command to see some information of the file. The result of file command looked like this:
image

So, this is an ELF 64-bit executable for x86-64 systems and dynamically linked and also not stripped, this shows that the file isn't going to require any extra efforts to reverse engineer. Running the file, show this:

image

It printed a number and then it was waiting for input, doesn't makes much sense. I looked at the assembly of this file in rizin and tried to understand what's going on. Opening the binary in rizin with rizin -d -A bell, then looking all the functions with afl command looked like this:

image

Looks like we didn't got many functions to deal with, the challenge might be easy. Looking closely at this functions gave me a hint that this program might be generating some random numbers by taking the result of the time function as a seed to srand function.

image

Now, let's look into the disassembly of the main function by using the command pdf.

image

My assumption was true that it is taking the result of time function with was called without any parameters (as 0 was passed into rdi) as a seed to srand function, then rand function is called and then some mathematical operations are going on then printf is called prints the number that we saw when we ran the binary, in the end after printf, process function is called. Let's look at the disassembly of process function by using the command pdf @sym.process:

[0x55853a1629a9]> pdf @sym.process
            ; CALL XREF from main @ 0x55853a162a07
┌ sym.process (int64_t arg1);
│           ; var int64_t var_24h @ rbp-0x24
│           ; var int64_t var_20h @ rbp-0x20
│           ; var int64_t var_1ch @ rbp-0x1c
│           ; var int64_t var_18h @ rbp-0x18
│           ; var int64_t var_10h @ rbp-0x10
│           ; var int64_t var_8h @ rbp-0x8
│           ; arg int64_t arg1 @ rdi
│           0x55853a1628fc      push  rbp
│           0x55853a1628fd      mov   rbp, rsp
│           0x55853a162900      sub   rsp, 0x300x55853a162904      mov   dword [var_24h], edi             ; arg1
│           0x55853a162907      mov   rax, qword fs:[0x28]
│           0x55853a162910      mov   qword [var_8h], rax
│           0x55853a162914      xor   eax, eax
│           0x55853a162916      mov   dword [var_20h], 10x55853a16291d      mov   dword [var_1ch], 1
│       ┌─< 0x55853a162924      jmp   0x55853a162966
│      ┌──> 0x55853a162926      mov   edx, dword [var_1ch]
│      ╎│   0x55853a162929      mov   eax, dword [var_24h]
│      ╎│   0x55853a16292c      mov   esi, edx
│      ╎│   0x55853a16292e      mov   edi, eax
│      ╎│   0x55853a162930      call  sym.triangle
│      ╎│   0x55853a162935      mov   qword [var_10h], rax
│      ╎│   0x55853a162939      lea   rax, [var_18h]
│      ╎│   0x55853a16293d      mov   rsi, rax
│      ╎│   0x55853a162940      lea   rdi, [0x55853a162aa4]            ; "%ld"
│      ╎│   0x55853a162947      mov   eax, 0
│      ╎│   0x55853a16294c      call  sym.imp.__isoc99_scanf           ; int scanf(const char *format)
│      ╎│   0x55853a162951      mov   rax, qword [var_18h]
│      ╎│   0x55853a162955      cmp   qword [var_10h], rax
│     ┌───< 0x55853a162959      je    0x55853a162962
│     │╎│   0x55853a16295b      mov   dword [var_20h], 0
│     └───> 0x55853a162962      add   dword [var_1ch], 1
│      ╎│   ; CODE XREF from sym.process @ 0x55853a162924
│      ╎└─> 0x55853a162966      mov   eax, dword [var_1ch]
│      ╎    0x55853a162969      cmp   eax, dword [var_24h]
│      └──< 0x55853a16296c      jle   0x55853a1629260x55853a16296e      cmp   dword [var_20h], 1
│       ┌─< 0x55853a162972      jne   0x55853a162982
│       │   0x55853a162974      lea   rdi, str.cat_flag.txt            ; 0x55853a162aa8 ; "cat flag.txt"
│       │   0x55853a16297b      call  sym.imp.system                   ; int system(const char *string)
│      ┌──< 0x55853a162980      jmp   0x55853a16298e
│      │└─> 0x55853a162982      lea   rdi, str.Better_luck_next_time.  ; 0x55853a162ab5 ; "Better luck next time."
│      │    0x55853a162989      call  sym.imp.puts                     ; int puts(const char *s)
│      │    ; CODE XREF from sym.process @ 0x55853a162980
│      └──> 0x55853a16298e      mov   eax, 00x55853a162993      mov   rcx, qword [var_8h]
│           0x55853a162997      xor   rcx, qword fs:[0x28]
│       ┌─< 0x55853a1629a0      je    0x55853a1629a7
│       │   0x55853a1629a2      call  sym.imp.__stack_chk_fail         ; void __stack_chk_fail(void)
│       └─> 0x55853a1629a7      leave
└           0x55853a1629a8      ret
[0x55853a1629a9]>

So again, this function is performing some operations in a loop and calling another function triangle. It isn't a good idea to spend time on understanding assembly for hours in a live CTF, so I opened the binary in IDA Pro and the disassembly of main function looked like this:

int __cdecl main(int argc,
    const char ** argv,
        const char ** envp) {
    int current_time; // eax
    unsigned int rand_num; // [rsp+Ch] [rbp-4h]

    current_time = time(0 LL); // getting the current time since UTC 00:00:00, JAN 1, 1970 in seconds
    srand(current_time); // using the result of time function to set seed for rand function.
    rand_num = rand() % 5 + 8; // adding 8 to the result returned by the remainder of random number divided by 5.
    printf("%d\n", rand_num); // printing the random number.
    process(rand_num); // called the process function with rand_num variable as a parameter. 
    return 0;
}

The comments are written by me and the variables are also renamed by me not IDA :x, but we are more interested in the process function, it's disassembly looked like this:

__int64 __fastcall process(int argument_1)
{
  int won; 
  int i;
  __int64 our_input;
  __int64 triangle_result;
  unsigned __int64 v6;

  v6 = __readfsqword(0x28u);
  won = 1;
	
	// looping argument_1 (the random number that was passed in) times
  for ( i = 1; i <= argument_1; ++i )
  {
    triangle_result = triangle(argument_1, i);
    scanf(&unk_AA4, &our_input);
    if ( triangle_result != our_input )
      won = 0; 
  }

  if ( won == 1 )
    system("cat flag.txt");
  else
    puts("Better luck next time.");
  return 0LL;
}

If you know C, you would have recognised the code and it's pretty straight-forward, it is first looping the argument_1 times and calling the triangle function with argument one and the loop counter, and then asking for input then checking if the result from the triangle function is not equal to our_input, if it's not equal then it will set won to 0 (which is set to 1 by default) and at last the function if checking won is equal to 1, if it's then it will print the flag else we won't get the flag. So, the only thing left is to reverse engineer the triangle function and find out what it's doing and then make a script that can automate this for us. The C pseudocode of triangle function looked like this:

__int64 __fastcall triangle(unsigned int argument_1, int argument_2)
{
  __int64 v3; // rbx

  if ( argument_2 > (int)argument_1 )
    return 0LL;
  if ( argument_1 == 1 && argument_2 == 1 )
    return 1LL;
  if ( argument_2 == 1 )
    return triangle(argument_1 - 1, argument_1 - 1);
  v3 = triangle(argument_1, argument_2 - 1);
  return v3 + triangle(argument_1 - 1, argument_2 - 1);
}

This looked very straight-forward to me, it's just doing some checks and returning 0 or 1 on the basis of the results of this function, I made a same function in a c program to print the numbers in a loop that this function was producing, the c program looked like this:

#include <stdio.h>
#include <stdlib.h>

int triangle(int a1, int a2){
  int v3;

  if ( a2 > (int)a1 )
    return 0;
  if ( a1 == 1 && a2 == 1 )
    return 1;
  if ( a2 == 1 )
    return triangle(a1 - 1, a1 - 1);
  v3 = triangle(a1, a2 - 1);
  return v3 + triangle(a1 - 1, a2 - 1);

}

int main(int argc, char* argv[]){
    int arg = strtol(argv[1], NULL, 10); // converting the argv[1] into an integer
    for (int i=1; i <= arg; i++)
    {
      printf("%d\n", triangle(arg, i));
    }
}

Now, I just had to supply this c program the number returned by the binary and it will return the number sequence required by the binary to print the flag. Let's run the binary and test this.

image

So, I passed 11 to the C program and got the sequence!

image

I then supplied this to the binary:

image

Gotcha!, it tried to open flag.txt means It cleared the tests, now I just had to try it on the netcat server. Here is how it looks like:

image

4.2 Just In Time

image

I downloaded the file and ran it, it looked like this:

➜  JIT_ ./JIT
Decryption finished.

It just printed Decryption finished. and closed. The thing that everyone in the world does when they get a ELF binary is running ltrace and strace to see what's going on, and in some UNEXPECTED times you the flag there for a 300 POINTS Reverse Engineering challenge like most of the people got in this one.

JIT_ ltrace ./JIT
malloc(8)                                                               = 0x55f44ff2d2a0
malloc(8)                                                               = 0x55f44ff2d2c0
fopen("./JIT", "rb")                                                    = 0x55f44ff2d2e0
fread(0x55f44ff2d2c0, 8, 1, 0x55f44ff2d2e0)                             = 1
fclose(0x55f44ff2d2e0)                                                  = 0
strncpy(0x55f44ff2d2a0, "\177ELF\002\001\001", 8)                       = 0x55f44ff2d2a0
malloc(39)                                                              = 0x55f44ff2d4c0
strncpy(0x55f44ff2d4c0, "\033&8 yegHr($g1bKu{"f5`N}t#331Nv/%"..., 39)   = 0x55f44ff2d4c0
strlen("\033&8 yegHr($g1bKu{"f5`N}t#331Nv/%"...)                        = 38
puts("Decryption finished."Decryption finished.
)                                            = 21
malloc(39)                                                              = 0x55f44ff2d900
malloc(40)                                                              = 0x55f44ff2d930
strncpy(0x55f44ff2d900, "dctf{df77dbe0c407dd4a188e12013cc"..., 39)      = 0x55f44ff2d900
malloc(40)                                                              = 0x55f44ff2d960
strlen("\033&8 yegHr($g1bKu{\"f5`N}t#331Nv/%"...)                        = 38
free(0x55f44ff2d4c0)                                                    = <void>
free(0x55f44ff2d960)                                                    = <void>
free(0x55f44ff2d2a0)                                                    = <void>
+++ exited (status 0) +++

The string wasn't fully visible due to the string display limit of ltrace which can be changed with the -s switch, running ltrace -s 40.

JIT_ ltrace -s 40 ./JIT
malloc(8)                                                               = 0x5589b89612a0
malloc(8)                                                               = 0x5589b89612c0
fopen("./JIT", "rb")                                                    = 0x5589b89612e0
fread(0x5589b89612c0, 8, 1, 0x5589b89612e0)                             = 1
fclose(0x5589b89612e0)                                                  = 0
strncpy(0x5589b89612a0, "\177ELF\002\001\001", 8)                       = 0x5589b89612a0
malloc(39)                                                              = 0x5589b89614c0
strncpy(0x5589b89614c0, "\033&8 yegHr($g1bKu{"f5`N}t#331Nv/%`11F#1", 39) = 0x5589b89614c0
strlen("\033&8 yegHr($g1bKu{"f5`N}t#331Nv/%`11F#1")                     = 38
puts("Decryption finished."Decryption finished.
)                                            = 21
malloc(39)                                                              = 0x5589b8961900
malloc(40)                                                              = 0x5589b8961930
strncpy(0x5589b8961900, "dctf{df77dbe0c407dd4a188e12013ccb009f}", 39)   = 0x5589b8961900
malloc(40)                                                              = 0x5589b8961960
strlen("\033&8 yegHr($g1bKu{"f5`N}t#331Nv/%`11F#1")                     = 38
free(0x5589b89614c0)                                                    = <void>
free(0x5589b8961960)                                                    = <void>
free(0x5589b89612a0)                                                    = <void>
+++ exited (status 0) +++

AND BOOM WE GOT 300 POINTS!

5 Pwn

About

Writeups for dCTF 2021


Languages

Language:Python 49.7%Language:Perl 36.4%Language:Shell 11.2%Language:Dockerfile 1.5%Language:C 1.2%