Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

ngrok: expose a local service and detect its abuse

By updated

Improve this page

ngrok exposes a service running on your machine at a public URL on the Internet, through an outbound tunnel, without opening a single port or touching the firewall. It is a very quick way to show a demo or to test webhooks (the automatic calls that a third-party service, such as GitHub or Stripe, sends to a URL of your choice when an event occurs). For the same reason, it is also a tool that attacker groups hijack for exfiltration and persistence: a double-edged tool. This guide shows the legitimate use, tested end to end, the real exploits carried out with ngrok, and above all how to detect its abuse. For developers, administrators and security teams.

This lesson closes the module on publishing without an inbound port, with the most limited option: a temporary exposure for development (receiving a webhook, showing a demo), never a long-lived publication. To publish a service over time, the self-hosted option is Pangolin and the managed option is cloudflared. Choosing the family of tools is discussed in the Reverse proxy page.

What you will learn

  • Understand how an outbound tunnel bypasses the firewall by design.
  • Install the ngrok agent and expose a local service, with a real demonstration.
  • Expose HTTP and TCP, and declare your tunnels in a configuration file.
  • Decide when ngrok is suitable (development) and when it is not (production).
  • Recognize the real attacks carried out with ngrok and why it appeals to attackers.
  • Detect its use on a network: network, process and file indicators.

Prerequisites

  • a local service to expose (a web application, an SSH server, it does not matter);
  • an ngrok account: unlike the historical versions, the agent refuses to start without authentication;
  • access to a Linux, macOS or Windows terminal.

How ngrok works

ngrok is made of two parts: a worldwide cloud service hosted by the vendor, and a lightweight agent that you run next to the service to expose. The agent accepts no inbound connection. It opens a persistent outbound TLS connection to the ngrok cloud, and it is through this already established channel that public traffic comes back down to your machine.

The following diagram unrolls the sequence. Message ① is the ngrok agent opening the outbound connection; a visitor then reaches a public URL served by the ngrok cloud (②), which pushes the request into the agent's connection (③), and the agent forwards it to your local service (④). Your IP stays private, no port is opened, and the firewall only sees a perfectly ordinary outbound HTTPS connection.

The private network's connector opens an outbound tunnel to the public entry point; an HTTPS request from a visitor arrives at the entry point, comes back down through the tunnel to the connector, which calls the internal service; the response goes back up the same path.InternalserviceConnectorPublic entrypointVisitorno inbound port onthe private network① opens the tunnel(outboundconnection)② HTTPS request③ the request comesdown the tunnel④ call to the localservice⑤ response⑥ the response goesup the tunnel⑦ HTTPS response
For ngrok, the connector is the agent and the public entry point is the ngrok cloud. Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

This is precisely what makes ngrok both practical and dangerous: an outbound connection on port 443 looks like any other web traffic, and most firewalls let it through without a murmur.

Install ngrok

On Debian and Ubuntu, ngrok publishes a signed APT repository. We use it rather than a binary downloaded by hand, to benefit from updates and signature verification.

Terminal window
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
-o /etc/apt/keyrings/ngrok.asc
echo "deb [signed-by=/etc/apt/keyrings/ngrok.asc] https://ngrok-agent.s3.amazonaws.com bookworm main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update && sudo apt install ngrok

This form differs from the installation command that ngrok shows on its Linux download page (checked on 2026-10-25), which stores the key in /etc/apt/trusted.gpg.d/: a key placed there is accepted for any repository on the machine, whereas signed-by= restricts it to the ngrok repository alone (same approach as for Docker in the Pangolin page). The key is in armored text format (.asc): a test with apt 2.8.3 on Ubuntu 24.04 accepted it as is in signed-by=. On an older apt, this behavior remains to be checked.

The word bookworm may be surprising on an Ubuntu: it is the distribution name that ngrok gives in its command, and it designates a directory of the ngrok repository, not the release of your machine. The repository publishes the same package under several names of this kind (buster, bullseye, bookworm), and a test of apt update with this line did find the ngrok package on Ubuntu 24.04. If ngrok changes its line, the download page prevails.

Check the installed version:

Terminal window
ngrok version
# ngrok version 3.39.9

The output in this guide was captured with version 3.39.9: yours may be more recent, without changing the principle.

Authenticate

A tunnel without an account is impossible. The proof, if you try to expose a service without having registered a token:

Terminal window
ngrok http 8080
# ERR_NGROK_4018
# This ngrok session is not authenticated. ngrok requires an account
# and a valid credential to start a session.

Get your authtoken from the ngrok dashboard, then register it:

/home/user/.config/ngrok/ngrok.yml
ngrok config add-authtoken <YOUR_TOKEN>

The token is written to the agent's configuration file, located at ~/.config/ngrok/ngrok.yml on Linux. Never commit it to version control: it gives anyone who holds it the right to open tunnels on your account, hence a leak risk if the file ends up in a repository.

Expose a first service

Start a local service, here a simple Python web server on port 8080, which serves a minimal home page. In a first terminal:

Terminal window
mkdir -p ~/appli-locale && cd ~/appli-locale
echo '<h1>Mon appli locale</h1>' > index.html
python3 -m http.server 8080 --bind 127.0.0.1

The --bind 127.0.0.1 option keeps this server invisible from the network: only your machine reaches it. This is deliberate, because the ngrok tunnel starts from your machine and will be the only path of access from the Internet.

In a second terminal, open the tunnel:

Terminal window
ngrok http 8080

The agent displays a console that stays in the foreground. The useful information is there, notably the Forwarding line that contains your public URL:

ngrok
Session Status online
Version 3.39.9
Region Auto (lowest latency) (auto)
Web Interface http://127.0.0.1:4040
Forwarding https://imposing-escalator-gesture.ngrok-free.dev -> http://localhost:8080

This ngrok-free.dev URL is immediately reachable from the Internet. An incoming call goes through the tunnel and reaches your local service:

Terminal window
curl https://imposing-escalator-gesture.ngrok-free.dev/
# <h1>Mon appli locale</h1>

The Web Interface at http://127.0.0.1:4040 is a valuable tool: it inspects every request received in real time, which is ideal for debugging webhooks and replaying them. This is, by the way, the typical use of ngrok: receiving calls from Stripe, GitHub or a CRM during development, which cannot otherwise reach your localhost.

Why ngrok must not be used in production

ngrok suits a temporary exposure: a webhook to receive during development, a demo to show a customer. For a long-lived publication, it accumulates limits that rule it out. On the free plan, the domain is auto-generated and does not belong to you, the volume is capped, and the agent is proprietary: your traffic goes through a third party that you do not control, with no availability guarantee. A forgotten tunnel stays open as long as the agent runs, and then acts as a front door to your workstation.

Three habits frame legitimate use:

  • close the tunnel (Ctrl+C) as soon as the test is done, rather than leaving it running;
  • never expose a development service with default credentials or a real database behind it;
  • verify the signature of the webhooks received (GitHub's or Stripe's signature header), because the URL is public and anyone can send a request to it.

As soon as the need lasts more than a day, or touches real data, move to Pangolin, which keeps the control plane at home (what decides access, certificates and logs), or to cloudflared, which delegates it to Cloudflare with an identity layer. No open port does not mean no risk: that is the whole point of the "ngrok, a double-edged tool" section.

Expose other protocols

ngrok is not limited to HTTP. To expose a TCP service, for example an SSH server, use the tcp subcommand:

Terminal window
ngrok tcp 22

ngrok then assigns a TCP address of the form tcp://X.tcp.ngrok.io:PORT that you pass to your SSH client, which exposes an administration service on the Internet. This is exactly the mechanism that attackers hijack to expose an internal remote desktop, as we are about to see.

To manage several tunnels without remembering command lines, declare them in the configuration file, in v3 format:

~/.config/ngrok/ngrok.yml
version: "3"
agent:
authtoken: <YOUR_TOKEN>
endpoints:
- name: web
upstream:
url: 8080
- name: ssh
upstream:
url: 22

Then start a tunnel by its name, or all of them at once:

Terminal window
ngrok start web
ngrok start --all

Note the endpoints key: the old tunnels key belongs to the v2 configuration, which is deprecated.

ngrok, a double-edged tool

Everything above makes ngrok an excellent development tool. The same properties make it a formidable offensive tool, and this is not a hypothesis: ngrok is cataloged as S0508 in the MITRE ATT&CK framework, associated with five techniques including T1572 Protocol Tunneling (in French) and T1567 Exfiltration Over Web Service.

Why attackers like it: after compromising a machine, they have no inbound port to open and no firewall rule to change. An outbound HTTPS tunnel blends into legitimate traffic, and ngrok's infrastructure hides their own server.

Documented cases are plentiful:

  • The Akira ransomware, in a joint FBI/CISA advisory, establishes its command channel by tunneling through ngrok to bypass perimeter monitoring and exfiltrate data.
  • The APT28 group (BlueDelta), documented by Recorded Future, used ngrok to relay a credential-theft campaign and bypass two-factor authentication.
  • Sophos and Huntress, in their incident reports, describe attackers exposing internal RDP (ngrok tcp 3389), sometimes with the binary renamed to conhost.exe for evasion, and persistence through a scheduled task or a registry key.
  • Phishing campaigns host their fake banking pages behind ngrok subdomains, precisely because the vendor's legitimate domain inspires trust and bypasses filters.

The point to remember: ngrok is not malware, it is a legitimate tool whose encrypted outbound channel is the asset that attackers exploit. In the attack chain (the kill chain), it serves the command and control (in French) (C2) phase, the channel through which the attacker drives the compromised machine, detailed in the site's threat model.

Detect ngrok on a network

On the defense side, several indicators betray the use of ngrok. None is sufficient alone, they must be combined.

Network indicators. The agent's control channel targets domains such as connect.ngrok-agent.com and tunnel.<region>.ngrok.com. The exposed tunnels use *.ngrok-free.app, *.ngrok-free.dev, *.ngrok.app and the historical *.ngrok.io. A blocking rule must cover all these extensions on the ngrok name. Filtering by IP is fragile: ngrok relies on AWS, whose addresses are shared and changing. Favor DNS and SNI (the domain name that the client announces in clear at the start of a TLS connection, which allows filtering without decrypting).

Workstation indicators. The ngrok process with a telltale command line (tcp 3389, --authtoken, start --all), and the ~/.config/ngrok/ngrok.yml configuration file. Beware of binary renaming, already observed as conhost.exe. A detection query on processes, for example in KQL (the query language of Microsoft Defender):

DeviceProcessEvents
| where ProcessCommandLine has "ngrok"
and (ProcessCommandLine has "tcp 3389" or ProcessCommandLine has "authtoken")

Policy. In an enterprise information system, a user workstation generally has no legitimate reason to run ngrok. The defensible recommendation is to block it by default, through a DNS sinkhole (the name is resolved to a dead-end address) on the ngrok name with all extensions, complemented by an application rule blocking the unsigned binary, then to allow, on a documented exception, the teams that have a real need for it.

The alternatives

If you want to expose a service durably, or with a sovereignty constraint, a self-hosted solution gives you back control of the relay, and therefore of the logs and the domain name.

ToolModelFor what need
frpSelf-hosted (Go)Full-featured: TCP, UDP, HTTP, multiplexing
ratholeSelf-hosted (Rust)Same spirit as frp, lighter and faster
boreSelf-hosted (Rust)Minimalist, TCP only
ngrokSaaSDemos and webhooks in development, zero configuration

On this site, two neighboring tools address the same need from another angle: the Cloudflare tunnel with cloudflared, the managed option, and Pangolin, a fully self-hosted tunneled reverse proxy with access control. For remote access to your infrastructure rather than a public exposure, turn to a VPN such as WireGuard (in French) or Tailscale (in French). And to expose a local service without any third party, a reverse SSH tunnel (ssh -R) to a server that you control does the same job with OpenSSH.

Pangolin, cloudflared or ngrok: what to remember at the end of the module?

The three tools open the same outbound tunnel, so a single criterion really tells them apart: who operates the public entry point. With Pangolin, it is you: the entry point is a server of yours, you keep certificates, logs and identity, and you pay in operations. With cloudflared, it is Cloudflare, which terminates TLS and therefore sees the traffic in clear: you delegate the work and you accept this trust. With ngrok, it is the vendor, but the tool only targets a temporary development exposure, closed once the test is done: a need that lasts is never solved with it. So ask yourself two questions in order: does the service have to last, then do you accept that a third party sees the traffic? A "no" to the first leads to ngrok, two "yes" lead to cloudflared, and a "yes" followed by a "no" leads to Pangolin.

Key points

  • ngrok exposes a local service through an outbound tunnel, without opening a port: this is its strength and the risk it represents.
  • The agent is proprietary and your traffic goes through the vendor's infrastructure.
  • An account and an authtoken are mandatory: without them, the agent returns ERR_NGROK_4018.
  • The base command is ngrok http 8080; TCP goes through ngrok tcp, and multiple tunnels are declared as endpoints in ngrok.yml v3.
  • ngrok serves temporary exposure in development (webhooks, demos), never a long-lived publication: to last, choose Pangolin or cloudflared.
  • The free plan adds an interstitial page and caps at 3 tunnels, 1 GB and 20,000 requests per month.
  • ngrok is cataloged MITRE S0508: it serves exfiltration, C2 and persistence after compromise.
  • To detect it, combine DNS/SNI (all ngrok domains), the process and the config file; block it by default in the enterprise.

Knowledge check

Check that the essentials of this guide are mastered. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

FAQ: frequently asked questions about ngrok

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn