Posted on :: Tags:

Hi guys!,

This is Mj0ln1r and this is my first blog, writeup and first CTF as well. All new ? haha.

I started learning new skills and playing CTF's as well to gain hands on experience in the security field. Coming to this blog, its all about my solved challenges in InvaderCTF 2022.

InvaderCTF is a CTF competion which was organized by our collenge[RGUKTN] alumni those are working at great positions with the skills learned through CTF's. There is a list of categories in this CTF such as “web”, “pwn”, “reverse”, “cryptography”, and “miscellaneous”. And i solved some challenges from each category.

Crypto Challenges

Common....What?

common_modulus

I got a file named common_modulus.zip it has two files chall.py and output.txt. the chall.py script just prints the N e1 e2 c1 c2 the content of output.txt as follows.

N = 11982945131022410542351081395449872615892579857707579658716659690935488669385262821057859182557738914580246000223393286594124225383866984597532935421878496300855873841201081561776719850279196185513497651311088240409358040299378330842236508619359647972763016690363235765860969655129269784424956130539800284778318098141912923725687592311652722505056107470370398021165270753993680221146964650298810348339426550121843506831513763524799788245715184019272818769688806186156054217173423142297185080827697102885478690188900539745266957938792259348667098941846582939290347898221569129727818304319228531810884419349788595299183
e1 = 1432834983003528423789566679766
e2 = 2379308237310255832902020443526
c1 = 10689309714150831372003282520258034721869267911572516423408248565049962108650099748793151534577215410589895845939174468496094911105822340567352621464826482784496348432260039948367408369277304473142781582593382249759117725426180831722441987089651228047819100128903524486005240635239107861739718852670683772477033147265282652735461836031051746173537294339800736436758373421135499142186805931851613817214123606130652548146050084102387221849254771049043101744791081688090961965211538682034166530987653637019819142642682927570692406882796783114872064728299928706994667553634162223654351719854271521012272876869577548029865
c2 = 10108112864771204039110360647151162379625435403389064742046377050800935678884417470071380911451172735126940164631419702014060618271946963698795724980506620687308126757038560340598588393457958478150419444430669593694549750182242922247396011389187919036956934428645928391159497083109718312975799586599853937652754710111738660741391329300491640624992257712646153846113376883043637423386066176238663086142253925553012932883285101598565990266200395298234059134450705194609356310121298248102541581987639348408092513592224044341173092657291900970886956196149689937412107716004555806327078173298630211025335704973121968612105

What is common modulus attack?

A Common Modulus attack can be used to recover the plaintext when the same message is encrypted to two RSA keys that use the same modulus.

c1 = m^e1 % N
c2 = m^e2 % N

Assume that we find out a and b such that (e1 * a) + (e2 * b) = 1 then we can decode the plain text as (c1 ^ a) + (c2 ^ b). If we substitute how c1 and c2 is calculated to above equation, we can get m^(e1 * a + e2 * b) = m^1 = m

The Python script to perform common modulus attack.

from utilis import egcd,Convert
import gmpy2
from Crypto.Util.number import GCD

def neg_pow(a, b, n):
    assert b < 0
    assert GCD(a, n) == 1
    res = int(gmpy2.invert(a, n))
    res = pow(res, b*(-1), n)
    return res

def common_modulus(e1, e2, n, c1, c2):
	g, a, b = egcd(e1, e2)
	if a < 0:
		c1 = neg_pow(c1, a, n)
	else:
		c1 = pow(c1, a, n)
	if b < 0:
		c2 = neg_pow(c2, b, n)
	else:
		c2 = pow(c2, b, n)
	ct = c1*c2 % n
	m = int(gmpy2.iroot(ct, g)[0])
	return m

n = 11982945131022410542351081395449872615892579857707579658716659690935488669385262821057859182557738914580246000223393286594124225383866984597532935421878496300855873841201081561776719850279196185513497651311088240409358040299378330842236508619359647972763016690363235765860969655129269784424956130539800284778318098141912923725687592311652722505056107470370398021165270753993680221146964650298810348339426550121843506831513763524799788245715184019272818769688806186156054217173423142297185080827697102885478690188900539745266957938792259348667098941846582939290347898221569129727818304319228531810884419349788595299183
e1 = 1432834983003528423789566679766
e2 = 2379308237310255832902020443526
c1 = 10689309714150831372003282520258034721869267911572516423408248565049962108650099748793151534577215410589895845939174468496094911105822340567352621464826482784496348432260039948367408369277304473142781582593382249759117725426180831722441987089651228047819100128903524486005240635239107861739718852670683772477033147265282652735461836031051746173537294339800736436758373421135499142186805931851613817214123606130652548146050084102387221849254771049043101744791081688090961965211538682034166530987653637019819142642682927570692406882796783114872064728299928706994667553634162223654351719854271521012272876869577548029865
c2 = 10108112864771204039110360647151162379625435403389064742046377050800935678884417470071380911451172735126940164631419702014060618271946963698795724980506620687308126757038560340598588393457958478150419444430669593694549750182242922247396011389187919036956934428645928391159497083109718312975799586599853937652754710111738660741391329300491640624992257712646153846113376883043637423386066176238663086142253925553012932883285101598565990266200395298234059134450705194609356310121298248102541581987639348408092513592224044341173092657291900970886956196149689937412107716004555806327078173298630211025335704973121968612105

Convert(common_modulus(e1, e2, n, c1, c2))

Complete script available at github

When i run the script with the given N e1 e2 c1 c2 i got the flag.

PlainText in ascii : InvaderCTF{common_modulus_the_attack_name_is_common_modulus}

PWN Challenges

Format Strings

Can you exploit echo file to get the FLAG. nc 198.199.123.169 9003

Provided source code echo.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void initialize()
{
  alarm(60);
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

int echo(){
  char FLAG[] = "InvaderCTF{this_is_not_flag_flag_is_on_the_remote_server}";
  char buffer[0x20];
  puts("Can you leak the flag?");
  puts("Enter input to echo back:\n");
  read(0,buffer, 0x20);
  printf(buffer);
}
int main()
{
  initialize();
  echo();
}

First of all i executed the provided binary file echo.The program asks for a input. Observe the source program the input is going to be stored in buffer[0x20].The 0x20 is equals to 32 in decimal.

If i gave more than 32 characters as input a bufferoverflow will occur it may leads to an unintend outputs. lets see,

Format Strings

DOOOONE.....!

So, this is a simple bufferoverflow problem. In this way i got this flag ^_^

Flag : InvaderCTF{EZy_PZy_format_strings}

FoodCourt Overflow

Format Strings{: w="400" h="400"}

The name itself says that its a overflow problem again ^_~

The source code of the program running in the remote server is as follows

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int wallet = 200;

int order(char *item, int cost) {
    int n;
    printf("Input the number of %s you want to buy?\n", item);
    printf("> ");
    scanf("%d", &n);

    if (n > 0) {
        cost = cost * n;
        printf("That will cost Rs%d.\n", cost);
        if (cost <= wallet) {
            puts("Order placed!");
            wallet -= cost;
        } else {
            puts("Ah man, you don't have enough money to buy this order");
            n = 0;
        }
    } else {
        puts("Nah, buy something.");
    }

    return n;
}
void initialize()
{
  alarm(60);
  setvbuf(stdout, NULL, _IONBF, 0);
  setvbuf(stderr, NULL, _IONBF, 0);
  setvbuf(stdin, NULL, _IONBF, 0);
}

int main() {
    int item;
    puts("Welcome to RGUKT Food Court!");
    puts("We are giving free 200 RS wallet amount to our online customers.");
    puts("Sadly, you don't have enough money to buy the tastiest dish named Flag :/? Or is it? \n");

    while (1) {

        printf("Wallet Amount Rs%d.\n", wallet);
        puts("Menu: ");
        puts("1. Noodles: 50");
        puts("2. Biryani: 100");
        puts("3. Soft Drink: 20");
        puts("4. Flag: Rs 1000");
        puts("0. Logout\n");
        puts("Which item would you like to Order?");

        printf("> ");
        scanf("%d", &item);

        switch (item) {
            case 0:
                printf("Logging out");
                return 0;
            case 1:
                order("Nooooodles", 50);
                break;
            case 2:
                order("Dum Biryani", 100);
                break;
            case 3:
                order("Soft Drink", 1);
                break;
            case 4:
                if (order("buy the today's special dish - flag", 1000) > 0) {
                    FILE *fp = fopen("flag.txt", "r");
                    char flag[100];

                    if (fp == NULL) {
                        puts("Create flag.txt in the current working directory");
                        puts("Please report to admin if you saw this error on remote");
                        exit(1);
                    }

                    fgets(flag, sizeof(flag), fp);
                    puts(flag);
                }
                break;
            default:
                puts("Please select a valid item.");
        }
    }
}

The Main method is printing the menu and asking the user for the choice.Initially my wallet = 200.i.e balance. I am able to buy anything from the foodcourt except flag, cause it costs 1000. In order function i have to enter number of items i want to buy,i.e n. The cost is going to be multiplied with the number of items cost = cost * n. My balance then its going to place my order. i.e cost <= wallet. Then my balance is going to be update. wallet = wallet - cost.

Here i got an ideaaaa.. I see noodles costs 50 and it substracts the total cost from the wallet.but what if the cost is negative? Then the cost is going to be added to the wallet, and if cost is negative enough, then we can get a huge wallet balance.

How do we get a negative cost? Notice the cost is multiplied with n. cost = cost * n. So, that if we make the n as negative the cost becomes negative.

n is declared as integer , specially a signed integer. So i have to give a number out of signed int range.

signed int range for

  • 2 bytes(-32,768 to 32,767)
  • 4 bytes(-2,147,483,648 to 2,147,483,647)

The input is multiplied with 1000 and to get an overflow we have to enter a number which gives the result greater than 2,147,483,647. So, if i enter number greater than 21474835 the result will be in out of range.

Lets Get That Fa Fa Flag .. *_~

$ gcc food-court-overflow.c
$ ./a.out
Welcome to RGUKT Food Court!
We are giving free 200 RS wallet amount to our online customers.
Sadly, you dont have enough money to buy the tastiest dish named Flag :/? Or is it? 

Wallet Amount Rs200.
Menu: 
1. Noodles: 50
2. Biryani: 100
3. Soft Drink: 20
4. Flag: Rs 1000
0. Logout

Which item would you like to Order?
> 4
Input the number of buy the todays special dish - flag you want to buy?
> 21474836
That will cost Rs-480.
Order placed!
InvaderCTF{this_is_not_flag_flag_is_on_the_remote_server}
Wallet Amount Rs680.
Menu: 
1. Noodles: 50
2. Biryani: 100
3. Soft Drink: 20
4. Flag: Rs 1000
0. Logout

Which item would you like to Order?
>

I got the flag here but it says we have to do same process in the remote server to get the original flag.

The original flag : InvaderCTF{Enjoy_the_fine_F1aG_d1sh_01212124}

Reverse Engineering Challenges

Warmup

Really a simple warmup *_~

Warmup chall for beginners. Can we get source back from compiled binary ?

I got a binary file within the challenge

Lets see the strings in the binary file

$ strings warmup
/lib64/ld-linux-x86-64.so.2
libc.so.6
__isoc99_scanf
puts
__stack_chk_fail
printf
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.7
GLIBC_2.4
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u+UH
[]A\A]A^A_
InvaderCTF{pl4in_t3xt_s3cr3ts_0n_cl1ent_s1d3_c0de}
Enter flag : 
]%*c
Yepp, It is correct :)
Submit in https://ctf.pwn.af
Nope :(

Wait..What...? Sorry i forgot that its a warmup *_~

Flag = InvaderCTF{pl4in_t3xt_s3cr3ts_0n_cl1ent_s1d3_c0de}

Py Encrypter

Py encrypter

The code in the given source file

import random

# Two byte hash
def myHash(string):
    random.seed("H4shS33d" + string)
    num = random.getrandbits(16)
    return hex(num)[2:].zfill(4)

def encryptFlag(flag):
    enc = ""
    for char in flag:
        enc += myHash(char)
    return enc

flag = input("Enter flag : ")
enc = encryptFlag(flag)
print("Encrypted flag is : ", enc)

I know i have to reverse the hash to get the flag,but i did in a unintend way -_-

Unintend Solution

In the Challenge files they gave a hint that the flag is going to starts with InvaderCTF{

So, i tried to get the hash of the 'I' in using the given encryption script. Its the first 4 chars of the encrypted flag. I decided to seperate the encrypted string into group of 4 chars.

Then i did the bruteforce +_+. Sorry author! But i got the flag ,haha.

import random

# Two byte hash
def myHash(string):
    random.seed("H4shS33d" + string)
    num = random.getrandbits(16)
    return hex(num)[2:].zfill(4)

def encryptFlag(flag):
    enc = ""
    for char in flag:
        enc += myHash(char)
    return enc

brut = "ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz0123456789_"
hash = "1fb9,bdbf,bfa9,5295,262c,1fb9,17ac,7573,5295,6685,500e,bfa9,cf34,7573,d256,6685,bdbf,bfa9,cf34,bdbf,f2a3,0797,b15a,6685,6217,cf34,6685,0728,7573,262c,9082"
hash = hash.split(",")

flag = ""
for i in range(len(brut)):
    enc = encryptFlag(brut[i])
    for j in range(len(hash)):
        if enc == hash[j]:
            hash[j] = brut[i]
print("InvaderCTF{"+"".join(hash)+"}")

Flag : InvaderCTF{ch4ract3r_b4s3d_h4sh1ng_is_w3ak}

Intended Solution

I have to do it in other way am i correct?

Here it is..

import random
import string
flag = "08ef07973844262cd256a8635295ad53ece7518ae30f1fb9bdbfbfa95295262c1fb917ac757352956685500ebfa9cf347573d2566685bdbfbfa9cf34bdbff2a30797b15a66856217cf34668507287573262c908276b5"
flag = [flag[i:i+4] for i in range(0,len(flag),4)]
for j in flag:
    for i in string.printable:
        random.seed("H4shS33d"+i) 
        num = random.getrandbits(16)
        if int(j,16)==num:
            print(i,end="")
            break

Flag : InvaderCTF{ch4ract3r_b4s3d_h4sh1ng_is_w3ak}

CrackME

Crack me

In the attached files they gave me a crackme.pyc binary file. I used a online decompiler to get back the source code.

The source code of the binary file.


import random
random.seed(u'[5\x80E\x1d\x1aX\x91Z\x8f')

def encrypt(string):
    enc = []
    for char in string:
        temp = ord(char) + 120 ^ random.getrandbits(7)
        enc.append(temp)

    return bytearray(enc)


flag = input('Enter flag : ')
encFlag = encrypt(flag)
if encFlag == '\xd1\xe0\xb3\x9e\x80\xbf\xd3\x97\xa1\xda\x97\xdd\xe4\xef\xc9\xdf\x92\xff\xa2\xd5\x95\xfc\x99\xe6\xbc\xfa\xf5\xab\xd1\x89\xae\xd4\xe0\x94\xbb\x80\x96\x97\xa4\xd5\xd1\xe6\xce':
    print('Flag was right :)')
else:
    print('Nope')

The encrypted flag is

\xd1\xe0\xb3\x9e\x80\xbf\xd3\x97\xa1\xda\x97\xdd\xe4\xef\xc9\xdf\x92\xff\xa2\xd5\x95\xfc\x99\xe6\xbc\xfa\xf5\xab\xd1\x89\xae\xd4\e0\x94\xbb\x80\x96\x97\xa4\xd5\xd1\xe6\xce

I have to get the input char from the encrypted flag, the encryption is done with the XOR operation. And the seed is also same for every byte, so we can get flag char by

temp = ord(char) + 120 ^ random.getrandbits(7)
ord(char) = encryptedbyte ^ random.getrandbits(7) - 120

The script to get the flag is

import random
random.seed(u'[5\x80E\x1d\x1aX\x91Z\x8f')
enc_flag = l = list(b'\xd1\xe0\xb3\x9e\x80\xbf\xd3\x97\xa1\xda\x97\xdd\xe4\xef\xc9\xdf\x92\xff\xa2\xd5\x95\xfc\x99\xe6\xbc\xfa\xf5\xab\xd1\x89\xae\xd4\xe0\x94\xbb\x80\x96\x97\xa4\xd5\xd1\xe6\xce')

j = []
for i in l:
    temp = i ^ random.getrandbits(7)
    temp -= 120
    j.append(temp)

for i in j:
    print(chr(i),end="")

Flag : InvaderCTF{d3c0mpilati0n_m4kes_l1f3_e4si3r}


Thank you <3