Tags: hackimctf hackim 

Rating: 5.0

# HackIMCTF organized by Nullcon

### Finding Dora

Challenge is closed now, but you can find source code [here](https://github.com/nullcon/hackim-2020/tree/master/misc/dora).

``` nc misc.ctf.nullcon.net 8000 ```
Gives a text,
> Where's Dora? 1 for upper right, 2 for upper left, 3 for lower left, 4 for lower right

and

Gives a base64 string, on decoding we get a 720x720 png.
This is a randomly generated image.

#### Approach

+ In all images 5 gray characters are same, find all these 5 and replace with background color
+ Divide image in 4 quadrants
+ Find number of color pixels other than background color
+ The quadrant with dora image will have max color count, reply this answer to server **800** times :|

Divided code in 2 parts, one for connecting to server and sending answer and second to guess the answer from image.

Code worked without manual help.

To get code working I cropeed all the gray characters.

![Gray Ch1](./dora_imgs/fuckers/fucker0.png) ![Gray Ch2](./dora_imgs/fuckers/fucker1.png) ![Gray Ch3](./dora_imgs/fuckers/fucker2.png) ![Gray Ch4](./dora_imgs/fuckers/fucker3.png) ![Gray Ch5](./dora_imgs/fuckers/fucker4.png) ![Gray Ch6](./dora_imgs/fuckers/fucker5.png)

After removing all these gray character finding dora was easy.


Script ran for around 15 mins, and after 800 iterations got the flag.

![FLAG](./dora_imgs/flag.png)

**SCRIPTS**

main.py
```python
#!/usr/bin/python3
import socket
import os
from threading import Thread
from time import sleep
import glob
import img_rep as IR
import base64

#dora new
host="misc.ctf.nullcon.net"
#host="127.0.0.1"
port=8000

b64_buf=""

not_recvng_since=0

count_when_send=0

recv_count=0

def recv_p(s):

global b64_buf
global recv_count

while True:


data=s.recv(4096).decode().strip()
if len(data) < 5:
continue
b64_buf+=data
print("\rRcvd - "+str(len(data)),end="")

recv_count+=1

def combiner(s):
global b64_buf
global recv_count
global not_recvng_since
global count_when_send

prev_cnt=0
img_data=""
itrn=1
while True:
sleep(0.1)
#do some
if (recv_count != 0) and (recv_count != count_when_send) and (prev_cnt == recv_count):

not_recvng_since+=1

if not_recvng_since > 8:


#stopped recieving
temp_b64=b64_buf.strip()

fh=open("b64","w")
fh.write(temp_b64)
fh.close()

b64_buf=""
try:
img_data=base64.b64decode(temp_b64)
except:
print("base64 err\n generate files")
#if invalid base64 , fix it and pause script
input()

fh=open("img.png","wb")
fh.write(img_data)
fh.close()


#After sending input

try:
inpx1=IR.find_pos("img.png")
except:
pass

if inpx1 == 0:
print("need help")
os.system("export DISPLAY=:0;xdg-open img.png")
xin=input()
xin=str(xin).encode()
os.system("pkill ristretto")

#print("image ans "+str(inpx1))
if inpx1 !=0:
xin=str(inpx1).encode()

xin=xin+b"\n"
print("itrtn "+str(itrn))

s.send(xin)
itrn+=1

not_recvng_since=0
count_when_send=recv_count

prev_cnt=recv_count

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
print(s.recv(1024).decode())
t_read=Thread(target=recv_p,args=(s,))
t_read.start()

t_comb=Thread(target=combiner,args=(s,))
t_comb.start()
```

img_rep.py
```python
from PIL import Image,ImageFilter
import sys
import os
import cv2
import numpy as np

first_pixel=[]

def find_nco(img_p):
im = Image.open(img_p, 'r')
#im=im.filter(ImageFilter.GaussianBlur(radius=10))

R, G, B = im.convert('RGB').split()
r = R.load()
g = G.load()
b = B.load()
w, h = im.size

global first_pixel

nc=0
#clrs=set()

for i in range(w):
for j in range(h):
this_px=[r[i,j],g[i,j],b[i,j]]
if this_px != first_pixel:
nc+=1
# clrs.add(tuple(this_px))

return nc

def find_pos(filename):

image=cv2.imread(filename)
img_gray=cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
f=[]
for i in range(6):
template=cv2.imread("fuckers/fucker"+str(i)+".png",0)
result = cv2.matchTemplate(img_gray,template,cv2.TM_CCOEFF_NORMED)
f.append(np.unravel_index(result.argmax(),result.shape))

im=Image.open(filename,'r')
R, G, B = im.convert('RGB').split()
r = R.load()
g = G.load()
b = B.load()
w, h = im.size

global first_pixel
first_pixel=[r[2,2],g[2,2],b[2,2]]

print(first_pixel)
print(f)

#clrs=set() #if gray character found color 100x100 area with bg color
for x in range(5):
for j in range(f[x][0]-30,f[x][0]+70):
for i in range(f[x][1]-30,f[x][1]+70):
try:
r[i,j]=first_pixel[0]
g[i,j]=first_pixel[1]
b[i,j]=first_pixel[2]
except:
pass
im = Image.merge('RGB', (R, G, B))
im.save("clean.png")

os.system("convert -crop 360x360 clean.png tiles/tile%d.png")

counts=[]
#find_nco
#find no of colored pixels other than bg color
counts.append(find_nco("tiles/tile0.png"))
counts.append(find_nco("tiles/tile1.png"))
counts.append(find_nco("tiles/tile2.png"))
counts.append(find_nco("tiles/tile3.png"))

#print(counts)
#return
tmp_counts=counts[:]
tmp_counts.sort()

print()

print(abs(tmp_counts[3]-tmp_counts[2]))
#if top 2 count of nco is within range of 1000, then answer manually
if abs(tmp_counts[3]-tmp_counts[2]) < 1000:
#print(abs(tmp_counts[3]-tmp_counts[2]))
xyz=0
else:
s=counts.index(max(counts))
if s==0:
xyz=2
elif s==1:
xyz=1
elif s==2:
xyz=3
elif s==3:
xyz=4

return(xyz)

#print(find_pos(sys.argv[1]))
```

> For any queries/suggestions [@_r4n4](https://twitter.com/_r4n4)

Original writeup (https://github.com/aencode/general_writeup/blob/master/nullcon_hackim2020_dora.md).