prophet_'s blog

By prophet_, 4 years ago, In English

UPD: The RCPC token wall seems to have been removed. As of the time of this update, no additional security check is needed to access codeforces. Hence this script has been rendered obsolete by this change, and you will receive an error message if you attempt to run it.

As a result of a recent security update, many codeforces tools have been broken. In short, in order to access codeforces, a token is now required, which is decoded by the browser through a javascript implementation of AES. I have explained the specifics of this in my previous blog: https://mirror.codeforces.com/blog/entry/80070.

The token is different based on your IP address, and must be sent as a cookie with all requests to codeforces. Failing to do so will result in codeforces responding with a redirect page, containing the encrypted cipher token.

I have written an ad hoc script to decode this token using python.

https://github.com/great-prophet/adhoc_cf_rcpc_token_decoder

This script makes a request to codeforces to get the raw response with the AES cipher parameters, parses out the encrypted cipher, decodes it using an equivalent python AES implementation, and returns a hex formatted token. To use, simply run "source/cf_rcpc_token_decode.py" without input, with a python 3 interpreter.

Here is a screenshot of the expected behavior:

Expected Output

Note: You can ignore the "Cipher" output. It is the raw encrypted token, before decoding.

This token should be passed as a cookie with the key "RCPC" in all codeforces requests. For example, here is how you would do so with curl:

curl 'https://mirror.codeforces.com' -H 'Cookie: RCPC=606806bf70edce2cb8427ea1f7d71ece'

This script, by itself, will not automatically fix broken cf tools. You will need to have some technical knowledge in order to manually add the cookie as described above. This process is different for every tool. However I hope it can serve as a foundation for implementing a general fix.

Disclaimer: My current script is very very hacky and may be broken by future changes. However I will try my best to maintain it if an update does occur.

  • Vote: I like it
  • +57
  • Vote: I do not like it

»
4 years ago, # |
  Vote: I like it +4 Vote: I do not like it

Thanks ! My submission tool works now, i just need to pass the RCPC as a cookie while making the request

Here is the python sample

import requests
cookies = {
    'RCPC': '606806bf70edce2cb8427ea1f7d71ece',
}
response = requests.get('https://mirror.codeforces.com/', cookies=cookies)

Is this fix permanent btw?

  • »
    »
    4 years ago, # ^ |
      Vote: I like it 0 Vote: I do not like it

    Nice! I'm very happy to hear that my tool is working.

    I created this tool from open source data I've been able to gather as explained in my previous blog. As such, unfortunately I can not guarantee that this tool we remain functional if there are any new changes. Currently your guess is as good as mine as to when codeforces will update this system again.

    I will try to maintain this tool to the best of my abilities if an update does occur.

»
4 years ago, # |
  Vote: I like it +8 Vote: I do not like it

As I understood, "SlowAES" is just AES.

from Crypto.Cipher import AES
cypherkey = ''.join(map(chr, [233,238,75,3,193,208,130,41,135,24,93,39,188,162,51,120]))
iv = ''.join(map(chr, [24,143,175,219,224,248,126,240,252,40,16,213,179,227,71,5]))
mode = AES.MODE_CBC
aes = AES.new(cypherkey, IV=iv, mode=mode, segment_size=128)
orig_len = 16
ciph = '194d6e79956d10add11c9b2049c8e257'.decode('hex')
decr = aes.decrypt(ciph)
print decr.encode('hex')

gave me the same result

I believe that is worst "proof-of-work" could be invented. It relies on "security-by-obscurity", but does not guarantee that attacker will spend enough time on every request without attribution.

Usual "proof-of-work" scheme on CTFs: provide answer to question like "hash(salt + x)[:6] = 'abc012'". While we concerned to make proof-of-work not burdensome for mobile users and still heavy for attacker with GPU, I propose Argon2 as hash, tuned by #iterations for sub-1ms time for server and prefix for ~1s for mobile user. Salt is against pre-calc. Change cookie with (IP, day) pair looks resonable.

CF tools could obtain token for passing in cookies by external program (for example on Python).

  • »
    »
    4 years ago, # ^ |
    Rev. 2   Vote: I like it 0 Vote: I do not like it

    Thank you for your comment. Your implementation is much more elegant than mine. Do you if I use it to update mine?

    As a side note: My initial guess was also that "slow" referred to some sort of "proof-of-work" system. However I have later found that this code comes from: https://code.google.com/archive/p/slowaes/

    "Such implementations will be slow (hence the project name) but still useful when faster ones are not available"
    

    In actuality, from what I understand "slow" just refers to how it is implemented for interpreted languages (and is hence unintentional). I am still not completely sure what this "security feature" on codeforces is suppose to accomplish.

    • »
      »
      »
      4 years ago, # ^ |
        Vote: I like it +3 Vote: I do not like it

      Yes, you can.

      I assumed that there is nginx module for such anti-DDoS and there is — test_cookie with the same "SlowAES" scheme :) Looks like it is not proof-of-work, but just mitigation against stupid generic bots, which do not even use headless browser.

  • »
    »
    4 years ago, # ^ |
      Vote: I like it +3 Vote: I do not like it

    satoshi: invents bitcoin

    11 years later:

    • »
      »
      »
      4 years ago, # ^ |
      Rev. 2   Vote: I like it 0 Vote: I do not like it

      Miner rode across the river

      The miner sees — in the river the miner

      Put miner miner into miner

      Miner miner miner miner

»
3 years ago, # |
  Vote: I like it +3 Vote: I do not like it

It seems that CF added RCPC cookie again. Thanks a lot for your tool, it saved me from pain!

»
2 weeks ago, # |
  Vote: I like it 0 Vote: I do not like it

Is it still working? As of now I'm getting this error:

./cf_rcpc_token_decode.py
Traceback (most recent call last):
  File "/home/awez_mehtab/downloads/adhoc_cf_rcpc_token_decoder/source/./cf_rcpc_token_decode.py", line 52, in <module>
    raw_response = get_raw_cf_response()
  File "/home/awez_mehtab/downloads/adhoc_cf_rcpc_token_decoder/source/./cf_rcpc_token_decode.py", line 15, in get_raw_cf_response
    return str(urllib.request.urlopen("http://mirror.codeforces.com").read())
  File "/usr/lib/python3.10/urllib/request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/lib/python3.10/urllib/request.py", line 525, in open
    response = meth(req, response)
  File "/usr/lib/python3.10/urllib/request.py", line 634, in http_response
    response = self.parent.error(
  File "/usr/lib/python3.10/urllib/request.py", line 557, in error
    result = self._call_chain(*args)
  File "/usr/lib/python3.10/urllib/request.py", line 496, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.10/urllib/request.py", line 749, in http_error_302
    return self.parent.open(new, timeout=req.timeout)
  File "/usr/lib/python3.10/urllib/request.py", line 525, in open
    response = meth(req, response)
  File "/usr/lib/python3.10/urllib/request.py", line 634, in http_response
    response = self.parent.error(
  File "/usr/lib/python3.10/urllib/request.py", line 563, in error
    return self._call_chain(*args)
  File "/usr/lib/python3.10/urllib/request.py", line 496, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.10/urllib/request.py", line 643, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidden