After connecting to the server, we are in a bash. Let's try `getflag`, as suggested by the challenge description:
```bash: cannot set terminal process group (21183): Inappropriate ioctl for devicebash: no job control in this shellbash-5.0$ getflagThere are still 13 locks locked. No flag for you.bash-5.0$```
But what exactly is `getflag` anyway? Let's find out:
```bash-5.0$ type getflaggetflag is a shell builtinbash-5.0$```
This is interesting. They modified the bash and added a new builtin. Weneed that bash binary to figure out what's going on, so let's just dumpit:
```echo 'gzip -ck9 /bin/bash | base64' | nc ooobash.challenges.ooo 5000 > bash.gz.b64```
After removing the first few lines of output as well as the bash promptat the end, and decompressing the whole thing, we have the bash binary.Let's run it locally:
```% ./ooobash[error] token not found```
Weird. This shell wants to read a token file? Where is it located? Let'sfigure it out with `strace`:
```...openat(AT_FDCWD, "/etc/ooobash/token", O_RDONLY) = -1 ENOENT (No such file or directory)...```
I guess we need that file. Let's extract it with the same method:
```% echo 'cat /etc/ooobash/token | base64 -w0' | nc ooobash.challenges.ooo 5000bash: cannot set terminal process group (5001): Inappropriate ioctl for devicebash: no job control in this shellbash-5.0$ cat /etc/ooobash/token | base64 -w0cat: /etc/ooobash/token: Permission deniedbash-5.0$```
Now that's interesting. But the bash *did* read it. Let's try again:
```% echo 'cat /etc/ooobash/token | base64 -w0' | nc ooobash.challenges.ooo 5000bash: cannot set terminal process group (21216): Inappropriate ioctl for devicebash: no job control in this shellbash-5.0$ cat /etc/ooobash/token | base64H1kOFBxMYsjJZKgLb4q+sgPEpIFIvPmjrVwNfXpdDDA=bash-5.0$```
Weird. There seems to be some race condition, and if we are lucky, wecan read the file. Good. But maybe there is something else in that`/etc/ooobash` directory? Let's find out:
```% echo 'ls /etc/ooobash' | nc ooobash.challenges.ooo 5000bash: cannot set terminal process group (5022): Inappropriate ioctl for devicebash: no job control in this shellbash-5.0$ ls /etc/ooobashflagstatetokenbash-5.0$```
So let's extract the other two files too.
The flag file (base64):
```1bRuk/tqIIIoxPdyhBuerzktGaAkbZM/JRDfTOtNhVrP7bRkNL/b8lKVuPeoRcoqi9OfwJzlPsUL6r54nK2lJE87O6HTqEDSjTxoDrG+3pk=```
The state file (again base64):
```EiH5VzrDVKDKUYRLGs1bnYws9DmkkP9MUr+WOsT2+qE=```
Decoding those files only gives us seemingly random data, so how do we usethem? Clearly the ooobash binary knows how to do it, so let's reverseengineer that `getflag` builtin. The code of the builtin:
```cint getflag_builtin(){	FILE* file;	size_t flaglen;	char* flag;	char zero[32];	char out[104];
	memset(zero, 0, 32);	if(!memcmp(oootoken, zero, 32)) {		printf("[error] you need to execute this on the remote server\n");	} else if(leftnum <= 0) {		file = fopen("/etc/ooobash/flag", "rb");		if(!file) {			printf("[error]\n");			exit(1);		}		fseek(file, 0, 2);		flaglen = ftell(file);		fseek(file, 0, 0);		flag = (char *)malloc(flaglen + 1);		fread(flag, 1, flaglen, file);		fclose(file);		flag[flaglen] = 0;
		out[aes_decrypt(flag + 16, flaglen - 16, ooostate, flag, out)] = 0;		printf("You are now a certified bash reverser! The flag is %s\n", out);	} else {		printf("There are still %d locks locked. No flag for you.\n", leftnum);	}	return 0;}```
This shell builtin checks if all locks are unlocked, and then reads theflag, decrypts it with AES, and prints the result.
But what's that `ooostate`? Looking at the xrefs, we find anotherfunction, which seems to initialize the ooostate:
```cvoid init_ooostate(){	FILE* f;	int i;
	memset(ooostate, 0, 32);	f = fopen("/etc/ooobash/state", "rb");	if(!f) {		printf("[error] state not found\n");		exit(1);	}	fread(ooostate, 32, 1, f);	fclose(f);
	for(i = 0; i < LOCKSNUM; i++) {		locks[i] = 1;	}}```
And directly next to that function, there is another one:
```cvoid init_oootoken(){	FILE* f;
	memset(oootoken, 0, 32);	f = fopen("/etc/ooobash/token", "rb");	if(!f) {		printf("[error] token not found\n");		exit(1);	}	fread(oootoken, 32, 1, f);	fclose(f);}```
Those init functions read the token and the state. So all we have to donow is look at the `aes_decrypt` function, which looks like this:
```cint aes_decrypt(char* data, int len, char* key, char* iv, char* out){	EVP_CIPHER_CTX* ctx;	const EVP_CIPHER* type;	int outm;	int outl;
	if(!(ctx = EVP_CIPHER_CTX_new()))		handleErrors();	type = EVP_aes_256_cbc();	if(EVP_DecryptInit_ex(ctx, type, 0, key, iv) != 1)		handleErrors();	if(EVP_DecryptUpdate(ctx, out, &outl, data, len) != 1)		handleErrors();	if(EVP_DecryptFinal_ex(ctx, &out[outl], &outm) != 1)		handleErrors();
	EVP_CIPHER_CTX_free(ctx);	return outl + outm;}```
It's just a normal AES decryption routine, which decrypts the flag withthe `ooostate`.
So let's try to just make a simple C program which reads the three filesand calls `aes_decrypt`, without all the lock stuff. Turns out thatdoesn't work and we get an error:
```140595290311552:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:crypto/evp/evp_enc.c:583:```
Maybe we overlooked something? Yeah, we overlooked the `oootoken` whichisn't used so far, and we didn't realize that the `ooostate` is modifiedwhenever a lock is unlocked.
```cvoid update_ooostate(char* keyword, unsigned int idx){	size_t len;	int i;	char hash[32];	char buf[164];
	assert(strlen(keyword) < 100);	assert(idx < LOCKSNUM);	if(locks[idx]) {		locks[idx] = 0;		printf("unlocking %s (%d)\n", keyword, idx);		len = strlen(keyword);		memcpy(buf, oootoken, 32);		memcpy(buf + 32, keyword, len + 1);		memcpy(buf + 32 + len, oootoken, 32);		SHA256(buf, len + 64, hash);		for(i = 0; i < 32; i++)			ooostate[i] ^= hash[i];		leftnum--;	} else {		printf("lock %d was already unlocked\n", idx);	}}```
Now we just need those keywords. We can easily find them by checking allxrefs to `update_ooostate`. Those are the calls:
```cupdate_ooostate("unlockbabylock", 0);update_ooostate("badr3d1r", 1);update_ooostate("verysneaky", 2);update_ooostate("leetness", 3);update_ooostate("vneooo", 4);update_ooostate("eval", 5);update_ooostate("ret", 6);update_ooostate("n3t", 7);update_ooostate("sig", 8);update_ooostate("yo", 9);update_ooostate("aro", 10);update_ooostate("fnx", 11);update_ooostate("ifonly", 12);```
If we add this to our decoding program, after `init_ooostate` and`init_oootoken`, but before reading/decoding of the flag, we finally getthe flag:
```You are now a certified bash reverser! The flag is OOO{r3VEr51nG_b4sH_5Cr1P7s_I5_lAm3_bU7_R3vErs1Ng_b4SH_is_31337}```
The complete program code is available here:[solve.c](https://www.sigflag.at/assets/posts/ooobash/solve.c)
… and this is how you solve a pwn challenge without even interacting with the binary at all. This wasobviously not the intended solution, and it only worked because of therace condition which allowed us to sometimes read the key files.