Cyber Talents Mini CTF Week 1

Feb 08, 2021 • 8 minutes to read

Info

This was the first one of the Cyber Talents February weekly mini CTFs. Link here

There were 5 challenges:

challenges

Event Manager

event manager

Software products and services combine security information management and security event management. They provide real-time analysis of security alerts generated by applications and network hardware.

So the answer to this question was simply “SIEM”.

Funny that I only got this flag at the last because it wasn’t in a flag{xxxx} format and the question itself didn’t look like something to solve LOL.

Jett

jett

Ready to use your ultimate skills?

The file in the link can be found here

Overview

Having a glance of the code, we can see that the program is basically asking a user input, and check the user input against a piece of encrypted data, character by character.

We can see that this part is saving the encrypted text into memory:

        mov     DWORD PTR [rbp-160], 150
        mov     DWORD PTR [rbp-156], 204
        mov     DWORD PTR [rbp-152], 233
        mov     DWORD PTR [rbp-148], 31
        mov     DWORD PTR [rbp-144], 211
        mov     DWORD PTR [rbp-140], 75
        mov     DWORD PTR [rbp-136], 212
        mov     DWORD PTR [rbp-132], 74
        mov     DWORD PTR [rbp-128], 193
        mov     DWORD PTR [rbp-124], 215
        mov     DWORD PTR [rbp-120], 85
        mov     DWORD PTR [rbp-116], 176
        mov     DWORD PTR [rbp-112], 132
        mov     DWORD PTR [rbp-108], 215
        mov     DWORD PTR [rbp-104], 57
        mov     DWORD PTR [rbp-100], 222
        mov     DWORD PTR [rbp-96], 38
        mov     DWORD PTR [rbp-92], 75
        mov     DWORD PTR [rbp-88], 2
        mov     DWORD PTR [rbp-84], 139
        mov     DWORD PTR [rbp-80], 75
        mov     DWORD PTR [rbp-76], 21
        mov     DWORD PTR [rbp-72], 215
        mov     DWORD PTR [rbp-68], 21
        mov     DWORD PTR [rbp-64], 229

And the rest part of the code is prompting user input and check user input against the encrypted text in memory with a loop.

Within the loop, the most important part is the following:

        movzx   eax, BYTE PTR [rbp-48+rax]
        movsx   eax, al
        imul    eax, eax, 137
        cdq
        shr     edx, 24
        add     eax, edx
        movzx   eax, al
        sub     eax, edx
        mov     DWORD PTR [rbp-8], eax

This part is where the encryption is applied to a user input character (before it’s compared with the encrypted data in memory).

Encryption

Firstly, the program multiplies the character by 137 and save the result into eax(imul eax, eax, 137).

The use of the cdq command here confused me for a while. After digging the internet for a while, I discovered that the cdq instruction will populate edx register with either 0x00000000 or 0xFFFFFFFF according to the sign bit of eax.

Here’s a explanation on how cdq works which helped me while I was solving this challenge.

So after cdq instruction, the value of edx would be either 0xFFFFFFFF or 0x00000000.

In case of edx is 0xFFFFFFFF, the following shr edx, 24 instruction will make it 0x000000FF. Since it’s only the lowest 8 bits of eax that we care about (because movzx eax, al is gonna truncate it, more on this later), add eax, edx is equivalent to minus 1 from al, and sub eax, edx is equivalent to adding 1 to al. Thus those 5 instructions is nothing but truncate eax to the lowest byte.

While in case of edx is 0x00000000, it’s just adding 0, substracting 0 and truncating, which make it the same thing as in the former case.

Actually, in case of this challenge, the result of cdq command should always be edx = 0x00000000 if your input are all ASCII characters (bytes with values less than 127, i.e. sign bit is 0).

The following illustrates the process of a character being encrypted, assuming the character is { (0x7B):

        movzx   eax, BYTE PTR [rbp-48+rax] ; eax = 0x0000007B '{'
        movsx   eax, al                    ; eax = 0x0000007B '{'
        imul    eax, eax, 137              ; eax = 0x000041D3
        cdq                                ; edx = 0x00000000
        shr     edx, 24                    ; edx = 0x00000000
        add     eax, edx                   ; eax = 0x000041D3, adding 0, eax remains unchanged
        movzx   eax, al                    ; eax = 0x000000D3, TRUNCATED to the lowest 8 bits
        sub     eax, edx                   ; eax = 0x000000D3, substracting 0, eax remains unchanged
        mov     DWORD PTR [rbp-8], eax     ; saving eax to a variable for later comparison

Solution

With the observation above we can conclude that this “encryption” is equivalent to simply multiplying the character by 137 and truncate in into 1 byte.

Knowing this, we can map ASCII values to the values when they are encrypted. By reverse-applying this mapping to the cipher text we can decrypt the message.

The solution Python script:

# The encrypted message
c = [150, 204, 233, 31, 211, 75, 212, 74, 193, 215, 85, 176, 132, 215, 57, 222, 38, 75, 2, 139, 75, 21, 215, 21, 229] 

# The cipher/encryption look up table
cipher = [(i*137) & 0xff for i in range(128)] # [0, 137, 18, 155, 36, 173, 54, ..., 229, 110, 247]

# Decrypt
m = ''.join([chr(cipher.index(i)) for i in c])

print(m)

Keep_it_real

keep it real

Someone was attacking me he wants the flag, can you get it for me plzz?

The file in the link can be fonud here

This attached file is a Windows Events Log file. This was my first time even reading a Windows Event Log. I had no idea on what I should focus. So I just tried to sweep them through and try to spot special or unusual events.

There are events like commands being run, user login attempts, firewall rule configurations, user authentication, etc.

At near the end of the whole log, I found a suspicious command log involving a Powershell command:

suspicious

powershell -encodedcommand SQBuAHYAbwBrAGUALQBXAGUAYgBSAGUAcQB1AGUAcwB0ACAALQBVAHIAaQAgAGgAdAB0AHAAcwA6AC8ALwBkAG8AYwBzAC4AZwBvAG8AZwBsAGUALgBjAG8AbQAvAGQAbwBjAHUAbQBlAG4AdAAvAGQALwAxADEASABTAHoAVAB5ADQAcgByAEsAdwBwAHkAVQBwAE8ANgBWAHQAQgBVAFUARwB1ADkAVQA1AEcALQBHAHoATQBFADIATABrAC0ARgBwAFcAbwBCAFkALwBlAGQAaQB0AA==

Apparently someone’s invoking a Base64 encoded command in Powershell. Decoding the command I saw:

Invoke-WebRequest -Uri https://docs.google.com/document/d/11HSzTy4rrKwpyUpO6VtBUUGu9U5G-GzME2Lk-FpWoBY/edit

A URL is revealed! So I undoubtedly followed this URL and I saw a document on Google Docs:

flag

And there’s the flag.

F4c70r5

F4c70r5

Factorize all the way down.

The file in the link can be found here

Within the are 2 files: output.txt and chall.py.

chall.py

from Crypto.Util.number import *
import gmpy2
from flag import secret
p=getPrime(4096)
tmp=p+1
while True:
	if gmpy2.is_prime(tmp):
		q=tmp
		break
	tmp+=1

n=p*q
e=65537
print('n =', n)
print('e =',e)
enc = pow(bytes_to_long(secret.encode('utf-8')), e, n)
print('c =', enc)

output.py

n = 951752236915140832199748908693039643482784483902074328805152520574468385894787697421628889837312458972368822789183098930476868578994142902453764619470382186354755124562803268913310858946286402116516483754756602529140275651078834300546643264985329750684825417442514923821552633128625753457645015075044061956724052192812576201789794767301565010564202298988374151879060406294779207920467185412840082028372405791915383934456951737197975581056577455791036472946853658594411665332114506312107092278569171428558469601903172523050878720198012321065470355433469102888390983859620558787782721021089046454177636243251008530778652539935319177019860166186598073853688108173567866856964231247743467203884294661799001344528347401880203635793885635687603373996944778461477934238349308758143858756378007109795423094162767268604861217986574048575615695036605182467246663712698158708360994071228569740025875000061607591055998995791318294626029902423257925493557973396873086155141354215324268519945474274624518403980430243297107310646831850667409915421977427982814772841812364106728134725959018907476291114992862954747327639913039819011123510523468215933213307149052291041234690528992120953812506086775432947875150929032507470872383342321761152185862410954914695269256060010182103092293436220392161939322643220150850208969833274937053068973036583300776912495875448942037637954685467830888130579233843151020956996407127776535323875659902175256183919651900586231557101839033051692618183938054400883059654179603789649279342156436311107337135650837932570003864094755382785765132441275390932663926677067406875136312313026192349567835315882590179528261212886474357396988032402033951840198822593311717170345608440901322529031100463858003133206628129764268267186114478751138267868108177825787340514421124393463100710365602111990713166880105539662820515965997510336916057089919924127382268740016570933153882739481179823220194457498711735966237458544092166854492151855733680113669882834970917588467468450763773336882979228362194182442258322316805501041863297777969310334720546258597111615461312729100390612277069296070632654079517272872979055722183223194776869479328497670120352467722367536755524499589657444808423490113499222992115969009472046264319634133903375127278919109319803759184078586322967338305943072778915505052846361177654906506357532514407981608861738428484545619373078305491281976784140033166189850676357295084311575339612481840333505348160169849713312838754559337662281366047808411
e = 65537
c = 327077386348346101184096522158226652435706099109225871900230605984351117903800246594337858410998149918232043480026957704607340241003909393490401385485967743182982413825006847930414171836365770142856359887366316596069720587979267537855809519478307478497476670378851336046128690581039298872182315778241635987972130791221393116202794711834897336580404069432555731332417554769920971759422836093644474913072748419219775921177794703183463315887088800152508124613335592138959844405427877616162481782552685977904222219470836883362427047811047129537868670830271572458177982888737218480600629620866088954699453114643847442370426675902077649948684148966904979971879186350657529867576919878719798381783529320030879788710833303137705581868186648545623094120792531304439317096445770907381060231469831880099129024887979180049500212268418241730570329522546476427136143317324249361548308164803903878761206743509441852708440542456223079124860276831333647047599740892333186355672896520911048217823236552892724847829949668610259009435097223596719104078489761546715092840149323895747624504395584601579144904792920541825749864659143193662609460918063314733238927359300548944228296908818325053348886598442688500795391716150621274601216681553976683866131173458417009219427716408782474282016150081936949011159232183305456506255260128017517283847723434446687211648945191738498243855399126444288977984419400412388096293382856052759009813224850897743317819017365421420567063206322176282003030630564379588629232090789463210087919793870744942378934994203557879067720951637556039454580251080480853408218836150626461790953446312776137924265253529859339253679904558759148971395984135661351771162122128182000249840629158929879588788517248104174638728121624277244803476490932860470492654810980915069811506536290083966326945180552995137541908382419642851304697704419620393233170013467112815287659902036962453676681004661436019063891097253794023633083935593859211244178961282904838164034035503609018017085207813372743755106523870407300139090930471246246452348347608751309929620939053506778225492876252594499312034372725757786558330299216112395531067775533238799121023265242763734573994957531160562422049356773668682449519855511601566261028308457741280980377600910658293342522613412979271358590917850339008659636307394967716472776596347614221668364785104488251632443992650239493772549442105328396648365818985242371694074768517093063734552089273679040641390304309114286021181293539442153357310326761928632

I noticed that p and q are 2 adjacent prime numbers based on the code in chall.py. This means if I calculate the square root of n, it should be in between of p and q and shouldn’t be far from either of them.

My solution Python script:

n = 95175223691...
e = 65537
c = 32707738634...

import gmpy2
from math import ceil

gmpy2.get_context().precision = 4096
x = int(gmpy2.sqrt(n))

tmp = x
while True:
	if gmpy2.is_prime(tmp):
		q = tmp
		break
	tmp += 1

print('q: ', q)

tmp = x
while True:
	if gmpy2.is_prime(tmp):
		p = tmp
		break
	tmp -= 1

print('p: ', p)

def egcd(a, b):
	x,y, u,v = 0,1, 1,0
	while a != 0:
		q, r = b//a, b%a
		m, n = x-u*q, y-v*q
		b,a, x,y, u,v = a,r, u,v, m,n
		gcd = b
	return gcd, x, y

phi = (p - 1) * (q - 1)
_, d, __ = egcd(e,phi)

m = pow(c,d,n)
print(m.to_bytes(ceil(m.bit_length()), 'big').decode())

gh0st Us3r

gh0st Us3r

We are creating InfoSec blog to upload our recent work. If you faced any issues don’t hesitate to infor us.

Following the link, we are greeted by a welcome page.

welcome

Clicking the links on the top-left corner, the URLs caught my attention:

abuot

contact

The page name in the URLs is an argument for the page parameter. This means potential LFI. So I immediately tried out a URL:

/?page=../../../../../../../etc/passwd

Unfortunately, I didn’t get what I wanted:

lfi fail

Actually it was a GIF LOL:

403

Next I tried to fuzz with the argument, e.g. putting a single quote:

/?page=contact%27

fuzz

Oooh this is interesting. Seems like the value of the argument was put into another string, and the string was to be evaluated by assert(). And the code to be evaluated be like:

"strpos('$file', '..') === false"

Looks like this is the code stopped me from accessing /etc/passwd.

This means we can inject some PHP code for execution. So I tried this payload:

abc',+'zzz') === false && die(file_get_contents('index.php')) || strpos('aaa

This is the whole statement to be executed by assert():

strpos('abc',+'zzz') === false && die(file_get_contents('index.php')) || strpos('aaa', '..') === false

This will fetch the content of index.php for me.

lfi success

index.php

This confirms our findings from the error message.

At this point, let’s dial the time back a bit. In the source of the welcome page, I found some interesting comments:

hint home

It mentioned the /home directory twice. So I guess I should check the /home directory. This is the payload I used:

abc',+'zzz') === false && var_dump(scandir('../../../../home')) || strpos('aaa

I used var_dump() and scandir() functions to retrive a directory on the file system.

scandir home

So we can see there is one subdirectory under /home. Then I used a similar payload to fetch that directory:

scandir gh0st

Hmm. Nothing really special. Just normal user directory files. Well, I guess I’d better still check them. So first I picked .bash_history:

.bash_history

And viola! There’s a flag in it!

CTFCyberTalentsMini CTF
comments powered by Disqus