Safe Penetration Testing Labs · intermediate · ~13 min
Recognize regular call-home intervals in connection timestamps and report the beacon period.
Implants call home on a schedule, and that regularity is what betrays them. Human-driven traffic is bursty and irregular; an automated beacon produces connections separated by nearly constant intervals. Detecting it means looking at the gaps between sorted timestamps rather than the timestamps themselves: compute consecutive differences, then ask whether they are all close to a common value. This lesson works on a fixed array of timestamps — it analyses recorded connection metadata, not live traffic.
Beacon detection is one of the highest-value analytics in network defence, because it finds compromise from metadata alone — no payload inspection, no decryption. It also generalises: the same "is this sequence evenly spaced?" question detects polling loops, scheduled jobs and automated scrapers. And it is a good lesson in tolerance, because exact equality is useless against real data that always contains jitter.
Work on the gaps. Convert n timestamps into n-1 consecutive differences. Every property you care about lives in those gaps.
Jitter tolerance is mandatory. Real beacons deliberately randomise their interval slightly, and networks add delay regardless. Comparing gaps for exact equality finds nothing; each gap must be within +/- jitter of the reference.
Choose a reference deliberately. Comparing every gap to the first one is simple but sensitive to an unlucky first sample. Comparing to the median or the mode is more robust — worth knowing even when the simple version is what you implement.
The mode of the gaps. The most frequently occurring interval is the beacon period, and returning it tells the analyst how often the implant calls home, which is far more actionable than a boolean.
Need enough samples. Two timestamps produce one gap, which is trivially "regular". Require at least three or four before claiming a pattern; fewer is not evidence.
Sorted, positive gaps. The input must be sorted, and a non-positive gap means duplicate or out-of-order data — reject it rather than producing a nonsense verdict.
Regular is not malicious. Software updaters, monitoring agents and NTP clients all beacon perfectly. The signal narrows the field; attribution needs more evidence.
/* all consecutive gaps within +/- jitter of the first one? */
int is_beaconing(const int *ts_sorted, int n, int jitter) {
if (n < 3 || jitter < 0) return 0; /* too few samples to claim a pattern */
int d0 = ts_sorted[1] - ts_sorted[0];
if (d0 <= 0) return 0; /* unsorted or duplicate timestamps */
for (int i = 1; i < n - 1; i++) {
int d = ts_sorted[i+1] - ts_sorted[i];
if (d <= 0) return 0;
int diff = d - d0;
if (diff < 0) diff = -diff; /* absolute difference */
if (diff > jitter) return 0; /* one bad gap breaks the pattern */
}
return 1;
}
/* the most common gap - the beacon period */
int dominant_interval(const int *ts_sorted, int n) {
if (n < 2) return -1;
int m = n - 1, best = 0, best_count = -1;
for (int i = 0; i < m; i++) {
int d = ts_sorted[i+1] - ts_sorted[i], c = 0;
for (int j = 0; j < m; j++) if (ts_sorted[j+1] - ts_sorted[j] == d) c++;
if (c > best_count) { best_count = c; best = d; }
}
return best;
}
Key points:
n < 3 is a deliberate evidence threshold, not an arithmetic guard.<stdlib.h> just for abs.d <= 0 rejects unsorted or duplicated input rather than silently misreporting.Implanted malware 'beacons' to its command-and-control server on a schedule. Human traffic is bursty and irregular; a beacon is metronomic — even when the author adds random jitter.
The run below tests a series of connection times for regular spacing within a jitter tolerance, and reports the most common interval (the likely beacon period).
#include <stdio.h>
static int is_beaconing(const int*ts,int n,int j){if(n<3||j<0)return 0;int d0=ts[1]-ts[0];if(d0<=0)return 0;for(int i=1;i<n-1;i++){int d=ts[i+1]-ts[i];int df=d-d0;if(df<0)df=-df;if(df>j)return 0;}return 1;}
static int dominant_interval(const int*ts,int n){if(n<2)return -1;int m=n-1,best=0,bc=-1;for(int i=0;i<m;i++){int di=ts[i+1]-ts[i],c=0;for(int j=0;j<m;j++)if(ts[j+1]-ts[j]==di)c++;if(c>bc||(c==bc&&di<best)){bc=c;best=di;}}return best;}
int main(void){
int conns[] = {0, 61, 119, 182, 240, 301}; /* call-home times (s), ~60s apart */
int n = (int)(sizeof conns/sizeof conns[0]);
printf("beaconing (jitter 3)? : %s\n", is_beaconing(conns,n,3)?"YES":"no");
printf("dominant interval : %d s\n", dominant_interval(conns,n));
return 0;
}
| Step | Line | What happens |
|---|---|---|
| 1 | n < 3 |
Two timestamps give one gap, which is trivially regular — that is not evidence of beaconing. |
| 2 | d0 = ts[1] - ts[0] |
The reference interval every other gap is compared against. |
| 3 | d0 <= 0 |
A zero or negative first gap means the data is unsorted or duplicated; refuse to judge it. |
| 4 | diff = d - d0, then negate if negative |
The absolute deviation of this gap from the reference. |
| 5 | diff > jitter |
A single gap outside tolerance ends it — beaconing requires every interval to be regular. |
| 6 | dominant_interval |
Counts how often each gap value occurs and returns the most common, giving the analyst the actual period. |
Comparing each gap to the previous gap instead of to a fixed baseline; ignoring the need for at least three timestamps.
Compiler errors and warnings:
-Wsign-compare if the count and index types are mixed.Runtime symptoms:
d - d0 > jitter without taking the absolute value, so gaps that are too short pass.n < 3), so a pair of timestamps always qualifies.dominant_interval returns a rare gap. You compared counts with >= so later ties overwrite earlier ones, or you counted occurrences of the wrong value.Technique: build three arrays — perfectly regular, regular with small jitter, and clearly irregular — and confirm the verdicts. Then feed an unsorted array and check it is rejected rather than misjudged.
d <= 0 test is what turns "unsorted input" from a silently wrong answer into an explicit rejection — a defensive habit worth generalising.ts[i+1], so it must stop at n - 2. Running to n - 1 reads one element past the end.int values can overflow if the range is large; use a wider type or validate the range for real capture data.n < 2 the difference loop has no valid iterations at all.const int * keeps the captured metadata unmodified.Concrete uses: Network-security platforms score internal hosts for beaconing behaviour using exactly this metadata analysis — connection times to an external address, gaps, and jitter tolerance. It is one of the most reliable ways to find command-and-control channels without decrypting traffic. The same technique identifies polling loops in application logs, detects automated scrapers by request cadence, and spots misconfigured agents retrying on a fixed timer.
Professional best practices:
Beginner:
Intermediate:
1. (Beginner) Regularity verdict. Implement is_beaconing with absolute deviation and a minimum of three samples. Example: gaps of 60, 61, 59 with jitter 2 -> 1; gaps of 60, 300, 61 -> 0. Concepts: tolerance, evidence thresholds.
2. (Beginner) Dominant interval. Implement dominant_interval returning the most common gap. Example: -> 60. Concepts: mode of a sequence.
3. (Intermediate) Reject bad input. Add explicit rejection of unsorted and duplicated timestamps, and test both. Concepts: defensive preconditions.
4. (Intermediate) Median reference. Compare every gap against the median rather than the first, and construct a case where the two disagree. Concepts: robust statistics.
Beacon detection works on the gaps between sorted timestamps, not the timestamps themselves: compute consecutive differences and test whether all of them sit within a jitter tolerance of a reference interval. The tolerance is essential — real beacons randomise slightly and networks add delay, so exact equality finds nothing — and the deviation must be an absolute value or only over-long gaps get rejected. Require at least three samples before claiming a pattern, reject non-positive gaps as unsorted or duplicated input rather than judging them, and report the dominant interval so the analyst learns the period. Remember that updaters and monitoring agents beacon perfectly too, so this is a lead rather than a verdict.