25

测试 APP 抓不到数据包该怎么办

 3 years ago
source link: https://mp.weixin.qq.com/s/4eBDfFtAmQJAJdFaPzIezw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

vi2qMzB.png!mobile

最近几次测试 APP 时,遇到过几次非 http/https 通信的情况,burp、fiddler 等 http 代理工具都无法正常抓到包,经过分析发现 app 是通过 socket 通信的,所以写出来记录下。

socket 与 websocket

先来区别下 socket 与 websocket,因为我们在使用 burpsuite 和 fiddler 时,发现 burp 和 fiddler 都是可以抓 websocket 的,所以有必要先区别一下,从本质上来说二者关系并不大,甚至说没啥关系,盗用一张图来说明下二者关系,读者可自行百度、谷歌检索二者关系。

faEJvyy.png!mobile

socket 抓包思路

为了方便理解,我们自己可以实现一个简单的通过 socket 通信的 APP 和与之其对应的 Server,实现一个简单功能,客户端 APP 发送 socket 消息,模拟平时项目中 APP 调用 socket 相关接口通信,同时接收服务端下发的 socket 消息,客户端 APP 运行如下所示:

2MvAre.png!mobile

服务端通过 ServerSocket 构造器实现 socket 监听绑定即可,运行如下所示:

73EzqmY.png!mobile

以上,就简单实现了一个通过 socket 通信的 c/s,通过这种方式发送的数据包,burp 和 fiddler 之类的代理工具是无法抓到的,因为他们本来就属于 http/https/websocket 代理工具,对 socket 是无能为力的,所以我们需要换些思路。

tcpdump+wireshark

这种方式抓包非常通用,不光针对 socket 方式,http/https 等等也是可以的,因为这些两种抓包工具都是直接对流经网卡的数据包进行捕获,不存在区别信息传递使用什么协议,可以通过 tcpdump 将数据包保存成 pcap 格式,然后用 wireshark 打开进行分析,来看下用 tcpdump 抓到的数据包:

VN36Z36.png!mobile

客户端发送的数据包

iQZjE3Z.png!mobile

服务端接收的数据包

tcpdump 显示数据包格式不是很友好,导入到 wireshark 或者可用 wireshark 直接抓包,分析起来就比较容易了,可以看到数据传输是通过 socket 传输的:

RR3mQ3U.png!mobile

hook 方式抓包

上述方法虽然抓包很好,但是对于渗透测试来说,我们不仅仅要看到数据包内容,更重要的是还能修改数据包,所以这里还可以使用 hook 方式抓包,在实现 socket 通信的过程,客户端(基于 android6.0 系统)发送消息需要会调用 java.io.OutputStream.write()  方法,也有可能是  java.io.PrintWriter.write()  方法,这里以 java.io.OutputStream.write  方法为例,这个方法有三个重载,分别是  write(byte[] buffer) write(int oneByte) write(byte[] buffer, int offset, int count ),通过去阅读 android 源码,可以发现,三者调用关系为  write(byte[] buffer)-> write(byte[] buffer, int offset, int count)-> write(int oneByte) ,这里直接把源码粘贴过来,方便大家看:

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package java.io;

import java.util.Arrays;

/**
* A writable sink for bytes.
*
* <p>Most clients will use output streams that write data to the file system
* ({@link FileOutputStream}), the network ({@link java.net.Socket#getOutputStream()}/{@link
* java.net.HttpURLConnection#getOutputStream()}), or to an in-memory byte array
* ({@link ByteArrayOutputStream}).
*
* <p>Use {@link OutputStreamWriter} to adapt a byte stream like this one into a
* character stream.
*
* <p>Most clients should wrap their output stream with {@link
* BufferedOutputStream}. Callers that do only bulk writes may omit buffering.
*
* <h3>Subclassing OutputStream</h3>
* Subclasses that decorate another output stream should consider subclassing
* {@link FilterOutputStream}, which delegates all calls to the target output
* stream.
*
* <p>All output stream subclasses should override both {@link
* #write(int)} and {@link #write(byte[],int,int) write(byte[],int,int)}. The
* three argument overload is necessary for bulk access to the data. This is
* much more efficient than byte-by-byte access.
*
* @see InputStream
*/
public abstract class OutputStream implements Closeable, Flushable {

/**
* Default constructor.
*/
public OutputStream() {
}

/**
* Closes this stream. Implementations of this method should free any
* resources used by the stream. This implementation does nothing.
*
* @throws IOException
* if an error occurs while closing this stream.
*/
public void close() throws IOException {
/* empty */
}

/**
* Flushes this stream. Implementations of this method should ensure that
* any buffered data is written out. This implementation does nothing.
*
* @throws IOException
* if an error occurs while flushing this stream.
*/
public void flush() throws IOException {
/* empty */
}

/**
* Equivalent to {@code write(buffer, 0, buffer.length)}.
*/
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}

/**
* Writes {@code count} bytes from the byte array {@code buffer} starting at
* position {@code offset} to this stream.
*
* @param buffer
* the buffer to be written.
* @param offset
* the start position in {@code buffer} from where to get bytes.
* @param count
* the number of bytes from {@code buffer} to write to this
* stream.
* @throws IOException
* if an error occurs while writing to this stream.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code count < 0}, or if
* {@code offset + count} is bigger than the length of
* {@code buffer}.
*/
public void write(byte[] buffer, int offset, int count) throws IOException {
Arrays.checkOffsetAndCount(buffer.length, offset, count);
for (int i = offset; i < offset + count; i++) {
write(buffer[i]);
}
}

/**
* Writes a single byte to this stream. Only the least significant byte of
* the integer {@code oneByte} is written to the stream.
*
* @param oneByte
* the byte to be written.
* @throws IOException
* if an error occurs while writing to this stream.
*/
public abstract void write(int oneByte) throws IOException;

/**
* Returns true if this writer has encountered and suppressed an error. Used
* by PrintStreams as an alternative to checked exceptions.
*/
boolean checkError() {
return false;
}
}

所以理论上我们去 hook  write(byte[] buffer)  这个方法就可以了,这个 hook 代码代码非常简单,这里就不做展示了,可以看看 hook 结果:

iy6RF3i.png!mobile

到这里,能够 hook 到,就可以按照我们的需求来修改数据包了,当然,我们也需要找一个 APP 来实战下,在市场上的 APP 是否真的有效。

rYv2AjJ.png!mobile

objection

前一篇文章讲 objection 的使用,这里正好可以用 objection watch 一下  java.io.OutputStream  输出流,发现这里面有 close、flush、write 三个方法

ZJ36N3Z.png!mobile

Watch 一下 write 方法,观察 app 在处理业务时,是否有该方法的调用

muAb2q7.png!mobile

通过 objection 对 write 方法的跟踪,发现确实 socket 通信调用了 write 方法,而且通过堆栈信息,我们还发现了疑似发送数据包的方法,send、request,这里尝试 hook send 方法,发现果然是有用的,输出信息如下:

r6FfEvU.png!mobile

综上就是最近遇到的关于 socket 抓包的一点想法和实践,虽然平时测试很少遇到 socket 通信的,但是遇到了,就需要解决不是么?不知道大佬们还有没有更好的思路,如果有,还请告诉我。

m2EFvum.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK