I am interested in demoing printf vulnerabilities via an NDK app. To be clear, I am aware that to log in the console we can use __android_log_print(ANDROID_LOG_DEBUG, "LOG_TAG", "Print : %d %s",someVal, someStr);. I have tried it and I know it works. But I explicitly want to demo the vulnerabilities of printf(), specifically to use the %n specifier to write to a pointed location.
Is there a way to make printf() work to this effect or is it possible to achieve this via __android_log_print()? I attempted it with the android/log.h header but it didn't work.
I can get the app to crash by running something along the lines of printf(%s%s%s%s%s%s%s%s%s%s). But again, I can't manipulate pointers.
For general knowledge purposes, why is it that printf() doesn't work in the first place and how does __android_log_print() prevent these exploits?
You do realize that Android is open source.
Starting with looking for __android_log_print()
and finding it: https://android.googlesource.com/platform/system/core/+/refs/heads/master/liblog/logger_write.cpp
int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
va_list ap;
char buf[LOG_BUF_SIZE];
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
return __android_log_write(prio, tag, buf);
}
I eventually ended up looking at: https://android.googlesource.com/platform/bionic/+/refs/heads/master/libc/stdio/vfprintf.cpp
lines 453-454:
case 'n':
__fortify_fatal("%%n not allowed on Android");
Also referenced in the code is additional safety through FORTIFY which is described in the following blog post:
https://android-developers.googleblog.com/2017/04/fortify-in-android.html
Android specifically does not support %n format specifiers because they're vulnerable.
https://android.googlesource.com/platform/bionic/+/400b073ee38ecc2a38234261b221e3a7afc0498e/tests/stdio_test.cpp#328
Related
I am following the Google tips to implement a JNI layer between my Android app and my C++ library. It suggests to use the following code to register native methods when the library is loaded:
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
...
// Register your class' native methods.
static const JNINativeMethod methods[] = {
{"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},
{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},
};
int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));
...
}
I am quite new to C++ so I decided to use clang-tidy to ensure my C++ code is modern and safe. clang-tidy reports:
error: do not use reinterpret_cast [cppcoreguidelines-pro-type-reinterpret-cast,-warnings-as-errors]
According to the clang-tidy documentation:
cppcoreguidelines-pro-type-reinterpret-cast
This check flags all uses of reinterpret_cast in C++ code.
Use of these casts can violate type safety and cause the program to
access a variable that is actually of type X to be accessed as if it
were of an unrelated type Z.
This rule is part of the “Type safety” profile of the C++ Core
Guidelines, see
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-reinterpretcast.
So I have a few options:
Disable this check and risk using reinterpret_cast inappropriately elsewhere
Ignore the check everywhere where I need to use it and create a messy codebase
Find some alternative way of implementing this more safely
I would like to do 3 if it's possible but I'm not quite sure where to start.
It’s not clear why GetEnv wants a void** rather than a JNIEnv** (what other type would you use?), but you can avoid the reinterpret_cast and the concomitant undefined behavior(!) with a temporary variable:
void *venv;
if (vm->GetEnv(&venv, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
const auto env=static_cast<JNIEnv*>(venv);
Note that a static_cast from void* is every bit as dangerous as a reinterpret_cast, but its use here is correct, unavoidable, and shouldn’t produce linter warnings.
In the function pointer case there’s nothing to be done: reinterpret_cast is the only, correct choice (and for void* rather than some placeholder function pointer type like void (*)() its correctness is not guaranteed by C++, although it works widely and POSIX does guarantee it). You can of course hide that conversion in a function (template) so that the linter can be told to ignore only it, but make sure to use a clear name to avoid “hiding” the conversion.
Our Android software uses a virtual file system (VFS) for SQLite, which has been working correctly. Once we began using it with Android 6 (Marshmallow) all sorts of weird errors started to occur with large negative offsets being passed to ftruncate(), stack overflows, data corruptions, etc. Using readelf (among other tools), we eventually traced the problem to a change in the imports used by libsqlite.so: Lollipop and earlier import ftruncate and mmap, the newest libraries import ftruncate64 and mmap64. We "solved" the problem by changing which functions we use depending on the API version (Marshmallow is version 23):
/*
* Empirical testing of Tab S2 running Marshmallow revealed the SQLite
* unix_syscall table uses "ftruncate" and "mmap" as connection points,
* but the actual functions linked against are the *64 versions. This
* leads to stack corruption and all sorts of nasty errors as a result.
*/
if (getApiVersion() >= 23) // for Marshmallow
{ setUnixSystemCall(NULL, "ftruncate", our_ftruncate64);
setUnixSystemCall(NULL, "mmap", our_mmap64);
}
else // for Lollipop & older
{ setUnixSystemCall(NULL, "ftruncate", our_ftruncate);
setUnixSystemCall(NULL, "mmap", our_mmap);
}
Looking at the source code both from http://www.sqlite.org/2015/sqlite-amalgamation-3081002.zip and https://github.com/android/platform_external_sqlite/blob/master/dist/sqlite3.c all that the C source calls is ftruncate and mmap which makes our methodology "questionable" at best.
How does libsqlite.so import and use ftruncate64 and mmap64 where the source code only calls ftruncate and mmap? Are we not looking at the correct source code repository? Is there something going on at a link step? Did Marshmallow remove support for the non-64 bit versions of these functions?
It turns out the headers in the NDK don't exactly match the corresponding headers that the OS is built with!
Bionic: https://android.googlesource.com/platform/bionic.git/+/marshmallow-release/libc/include
Here's the way to BUILD the NDK: https://android.googlesource.com/platform/ndk/+/marshmallow-release
In particular,
https://android.googlesource.com/platform/bionic.git/+/marshmallow-release/libc/include/unistd.h
#if defined(__USE_FILE_OFFSET64)
extern int truncate(const char *, off_t) __RENAME(truncate64);
extern off_t lseek(int, off_t, int) __RENAME(lseek64);
extern ssize_t pread(int, void *, size_t, off_t) __RENAME(pread64);
extern ssize_t pwrite(int, const void *, size_t, off_t) __RENAME(pwrite64);
extern int ftruncate(int, off_t) __RENAME(ftruncate64);
https://android.googlesource.com/platform/bionic.git/+/marshmallow-release/libc/include/sys/mman.h has similar macros for mmap - the __RENAME() in the system headers means that any code built using the system headers (e.g., libc.so) will only export ftruncate64, not ftruncate, and when an app that calls ftruncate is linked against libc.so, it instead imports ftruncate64 rather than the call the source code was written with.
We didn't dive into the __RENAME() macro to investigate how this magic happens - the reality of trying to get a product out the door prohibits how deep we can go down the rabbit hole. If anybody wants to investigate this further, however, this is where you start.
When I use LKM to hook syscall about Android Kernel,I get something wrong
but I'm not good at Linux kernel debug,and I want to know how to deal with this problem.The similar problem told me the reason is read is blocked but module has been rmmod,I want to know how to solve this problem.
The reason is I want to monitor the apk's behavior.so I choose hook Linux kernel 3.4.67(goldfish) and with Android 4.4.4 code complied
Here is My code,when I insmod the module,it really get the action about read sth. But when I rommd the module ,the emulator direct get oops,and I finally find the reason is in my sys_read() hook,because when I hook syscall open ,it's okay.
#include <linux/kernel.h>
#include <linux/module.h>
#include<linux/init.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <linux/moduleparam.h>
#include <asm/cacheflush.h>
#include<linux/delay.h>
#include<linux/file.h>
#include<linux/fs.h>
#include<linux/dirent.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SafeCode han");
void ** sys_call_table;
asmlinkage int (*original_call_read) (unsigned int, char*, int);
asmlinkage int hack_sys_read(unsigned int fd, char * buf, int count){
if(fd == 0 && count == 1)
{
printk("something is read aha %s\n", buf);
}
return original_call_read(fd,buf count);
}
int init_module()
{
//this is my sys_call_table address which found in System.map
sys_call_table =(void*)0xc000d984;
printk(KERN_ALERT "testhahaha\n");
original_call_read = sys_call_table[__NR_read];
sys_call_table[__NR_read] = hack_sys_read;
return 0;
}
/*when I need to rommd my module ,the devices oops..*/
void cleanup_module()
{
sys_call_table[__NR_read]=original_call_read;
//sys_call_table[__NR_open]=original_call_open;
}
so ,Plz help me~ thanks~,I want to know the depth reason and the solution
It is unsafe to revert previous value of the syscall's pointer and unload the module, contained replacement code: some process may use your syscall function at this moment, and it will be very upset when its code is gone off. As far as I know, there is no protection against that.
Syscalls are not intended to be changed by kernel modules. Either patch kernel code itself, or do not touch syscalls at all and use some sort of hooks, e.g. kprobes.
while looking in android logging frame work -
I checked /dev/log/main |system |event log files. in these log file I dont see time stamp. but at the same time "logcat -v time" display time stamp along with log.
I check Logcat code Its reading androidlog_entry from /dev/log/* buffers and displaying on cmdline.
While tracing android logging code , I could not find at what point we are adding timestamp with our log.
I did trace following flow -
LOGI("pre_alloc_cap_mem_thread_init: inited=%d", obj->mPreAllocCapMemInited);
#define LOGE ALOGE
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, VA_ARGS))
#define LOG_PRI(priority, tag, ...) \
android_printLog(priority, tag, VA_ARGS)
#define android_printLog(prio, tag, fmt...) \
__android_log_print(prio, tag, fmt)
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
va_list ap;
char buf[LOG_BUF_SIZE];
va_start(ap, fmt);
vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
va_end(ap);
return __android_log_write(prio, tag, buf);
}
and on and on till NR_writev system call .
can somebody please guide me, when Its adding timestamp to android logs.
I appreciate help.. what ever i could get ..
But i have figure out it -
"Android frame work does not add time stamp, threadid etc. to android logs that goes to /dev/log/, but It rather pass it down to kernel to logger driver(check logger.c in kernel code) that further add up the timestamp+PID etc. as prefix to each android log. "
I have spent a ridiculous amount of time trying to figure this out and I am at an absolute loss.
I am working with the JUCE library and have modified one of their sample projects. My goal is to have a very simple Android app that is written in C++ and then ported to Android. I need a function in C++ that I can call that will then call a function on the Android side that will return my heap size and other characteristics to my C++ code so that I can manage memory there.
If anyone has a simple solution that would be amazing. Right now my current snag is this:
char const buf[] = "From JNI";
jstring jstr = env->NewStringUTF(buf);
jclass clazz = env->FindClass("android/os/Debug");
But I keep getting an error saying that 'NewStringUTF' is not a _JNIEnv member... but if I right click on the method and jump to the definition, I see it in my jni.h file... any suggestions? I'm working in Xcode by the way...
Is it plain C, not C++? Perhaps your file has a .c extension.
If it's plain C it should be
JNIEnv* env;
JNI_CreateJavaVM(&jvm, (void **)&env, &args);
(*env)->NewStringUTF(env, buf);