Whenever I install and start frida-server, I have a banking app that behaves weirdly. After some research, I discovered some app may be able to do a port scan on the device running frida-server at the NDK level. I tried changing frida-server
's default port to something else but I'm still reproducing the app's exact behavior.
According to mullerberndt, I have put together a script that checks for both frida-server's default port and the whole range of ports and send a D-Bus protocol probe
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
struct sockaddr_in sa;
int detect_frida_server(void);
bool check_protocol(int);
bool check_all_ports(int);
int main() {
int res = detect_frida_server();
printf("Detection result return value: %d\n", res);
return 0;
}
int detect_frida_server() {
const int fs_port = 27047;
memset(&sa, 0 , sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(fs_port);
inet_aton("127.0.0.1", &(sa.sin_addr));
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(connect(sock, (struct sockaddr *) &sa, sizeof sa) != -1) {
printf("FRIDA DETECTION[1]: Open port: %d\n", fs_port);
if(check_protocol(sock)) {
printf("Frida server detected!");
return 1;
}
printf("Suspicious.\n");
return 2;
}
else if(check_all_ports(fs_port)) {
printf("Frida server detected!");
return 1;
}
if(check_all_ports(fs_port)) {
printf("Frida server detected!");
return 1;
}
return 0;
}
bool check_all_ports(int port) {
int i, sock;
for (i = 0; i <=65535; i++) {
if(i == port) continue;
sock = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_port = htons(i);
if(connect(sock, (struct sockaddr *) &sa, sizeof sa) != -1)
{
printf("FRIDA DETECTION[1]: Open port: %d\n", i);
if(check_protocol(sock)) return true;
}
}
return false;
}
bool check_protocol(int s) {
char res[7];
bool rres = false;
memset((void*) res, 0, sizeof(res));
send(s, "\x00", 1, NULL);
send(s, "AUTH\r\n", 6, NULL);
usleep(100);
if(recv(s, res, sizeof(res) - 1, MSG_DONTWAIT) != -1)
if(strcmp(res, "REJECT") == 0)
rres = true;
return rres;
}
Compiled it with
\Android\Sdk\ndk\21.4.7075529\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang detect-fs.c -o detect_frida
And push with
adb push detect_frida /data/local/tmp/detect-fs-v2
When running the code, I was able to detect the open port and connect
but the D-Bus protocol didn't seem to be working, hence stuck in a loop (if connected it would print Frida detected"):
So I trimmed down the code to just connecting to port and returning a true/false value:
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
int port_scan(void);
int main() {
int open = port_scan();
if(open) printf("Port is open at: %d\n", open);
else printf("No ports were found to be open. System secure.\n");
return 0;
}
int port_scan() {
struct sockaddr_in sa;
memset(&sa, 0 , sizeof(sa));
sa.sin_family = AF_INET;
for(int i = 0; i < 65535; i++) {
int sock = socket(AF_INET, SOCK_STREAM, 0);
sa.sin_port = htons(i);
if(connect(sock, (struct sockaddr *) &sa, sizeof sa) != -1) {
close(sock);
return i;
}
}
return 0;
}
without the connection protocol. I suspect this is what my app of interest was doing. To be fair, I gave my binary root access and did chmod 775
on the binary. But I do wonder if Play store apps would be able to detect this in a non-rooted environment at the NDK level without going through the hassle of trying to build an apk to test myself.
Is it reasonable to expect that no ports should be allowed to listen on localhost in a non-rooted android environment? (Not even a web server on port 80)? Well, since the app didn't outright complain root i know its capable for the most outright cases, reasonable to assume it was giving me a benefit of doubt but hinting my environment isn't secure.
So apart from mullerberndt's method of port scanning for frida-server, the alternative would be checking for frida-gadget, which we aren't using, how could we write a frida script to hook connect method to return false (ie all ports are closed). Keep in mind we have to allow legitimate connect methods as this is the fundamental of app communication methodology with its own backend
Also, how long would scanning all ports take on an android device to be discretely practical as a means of root/tamper checking?
On further research, I was actually found to be running 2 instances of frida-server
one on a default port the other on a custom port. I had to run the first port scanner script on my device and wait for it to finish. It took me about 5-10 mins on Termux.
Simply starting the server on a different port and making sure you dont have any other instances of frida running on the original port solves the issue, my app ceases to exhibit the weird behavior. For obvious reasons, I will neither state the bank app in question nor the abnormal behaviour of the app in response. Banks consider running their apps in a rooted environment a very sensitive security issue.
I just conclude my target app was simply actually checking to see if frida was listening on the default port 27047 with this code
boolean is_frida_server_listening() {
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(27047);
inet_aton("127.0.0.1", &(sa.sin_addr));
int sock = socket(AF_INET , SOCK_STREAM , 0);
if (connect(sock , (struct sockaddr*)&sa , sizeof sa) != -1) {
/* Frida server detected. Do something… */
}
}
Source: The Jiu-Jitsu of Detecting Frida (archived)
Which answers my question of how much time it would take to run a full port scan on all the 65535 ports. Basically 5 - 10 mins on an arm-64 Android device. Not a good option for a root detector that simply needs to complete their checks in seconds. When you can simply just change the port. And the creators of frida have changed their hand-shake protocol so that isn't working now.
Hope this helps anyone out there