本文为看雪论优秀文章
看雪论坛作者ID:勇士小蓝
0x01 前言
0x02 安装环境所需的依赖
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo vi /etc/apt/sources.list
# vi 内运行以下两条命令
:% s/us.archive.ubuntu.com/mirrors.aliyun.com/g
:wq
sudo apt-get install git vim wget curl python python3 python-pip python3-pip binutils file wget rpm2cpio cpio zstd nginx libffi6 libffi-dev software-properties-common -y
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get update
sudo apt-get install python3.6
sudo curl https://bootstrap.pypa.io/get-pip.py -o /get-pip.py
sudo python3.6 /get-pip.py
sudo ln -s /usr/local/bin/pip /usr/bin/pip
sudo pip install elasticsearch
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://<镜像加速器ID>.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo npm install -g n
sudo n latest
0x03 获取 libc-database 项目并处理
import React, { useState, useEffect, useCallback, useRef } from 'react';
import './App.css';
import 'fontsource-roboto';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import Link from '@material-ui/core/Link';
import CircularProgress from '@material-ui/core/CircularProgress';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableRow from '@material-ui/core/TableRow';
import { makeStyles } from '@material-ui/core/styles';
- const API_BASE = 'https://libc.rip/api';
+ const API_BASE = 'http://127.0.0.1/api';
const api = async (path, data) => {
let resp = await fetch(`${API_BASE}${path}`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
});
return await resp.json();
};
const useStyles = makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
},
'& .MuiButton-root': {
margin: theme.spacing(1),
},
'& .remove': {
marginTop: '1.2rem',
height: '2rem',
},
'& .findbutton': {
marginTop: '1.2rem',
},
},
table: {
marginTop: '1rem',
marginBottom: '1rem',
}
}));
function SearchRow({ onChange = () => {}, onRemove = () => {} }) {
const [symbol, setSymbol] = useState("");
const [address, setAddress] = useState("");
const [addressValid, setAddressValid] = useState(true);
const onSymbolChange = useCallback((evt) => {
setSymbol(evt.target.value);
}, []);
const onAddressChange = useCallback((evt) => {
setAddress(evt.target.value);
}, []);
useEffect(() => {
const valid = !!address.match(/^(0x)?[0-9a-fA-F]*$/);
setAddressValid(valid);
onChange({valid, symbol, address});
}, [address, symbol, onChange]);
return (
<div>
<TextField label="Symbol name" value={symbol} onChange={onSymbolChange} />
<TextField label="Address" error={!addressValid} value={address} onChange={onAddressChange} />
<Button className="remove" variant="contained" color="secondary" onClick={onRemove}>
Remove
</Button>
</div>
);
}
function SearchForm({ onSearch = () => {} }) {
const classes = useStyles();
const [nextId, setNextId] = useState(0);
const [rows, setRows] = useState([]);
const [states, setStates] = useState({});
const onRemoveRef = useRef();
const onChangeRef = useRef();
const makeRow = (id) => {
return (
<SearchRow key={id}
onRemove={() => onRemoveRef.current(id)}
onChange={(obj) => onChangeRef.current(id, obj)} />);
};
const isEmpty = useCallback((i) => {
let state = states[rows[i].key];
return !state || (!state.symbol && !state.address);
}, [rows, states]);
// Add new empty rows automatically
useEffect(() => {
let need = true;
for (let i = 0; i < rows.length; ++i) {
if (isEmpty(i)) {
need = false;
break;
}
}
if (need) {
setRows(rows => rows.concat([makeRow('' + nextId)]));
setNextId(id => id + 1);
}
}, [rows, states, nextId, isEmpty]);
// Remove superfluous rows at the end
useEffect(() => {
let i = rows.length - 1;
while (i >= 1 && isEmpty(i) && isEmpty(i-1)) {
--i;
}
if (i < rows.length - 1) {
setRows(rows => rows.slice(0, i+1));
}
}, [rows, states, nextId, isEmpty]);
const onRemove = useCallback((id) => {
for (let i = 0; i < rows.length; ++i) {
if (rows[i].key === id) {
setRows(rows.slice(0, i).concat(rows.slice(i+1)));
return;
}
}
}, [rows]);
const onChange = useCallback((id, obj) => {
setStates({...states, [id]: obj});
}, [states]);
onChangeRef.current = onChange;
onRemoveRef.current = onRemove;
const onSubmit = useCallback(() => {
let symbols = {};
for (let row of rows) {
let state = states[row.key];
if (state && state.valid && state.address && state.symbol) {
symbols[state.symbol] = state.address;
}
}
onSearch({"symbols": symbols});
}, [rows, states, onSearch]);
const isValid = useCallback(() => {
let cnt = 0;
for (let row of rows) {
let state = states[row.key];
if (!state)
continue;
if (!state.valid)
return false;
if (state.address && state.symbol)
cnt++;
}
return cnt > 0;
}, [rows, states]);
return (
<form className={classes.root}>
{rows}
<div>
<Button
disabled={!isValid()}
variant="contained"
className="findbutton"
color="primary"
onClick={onSubmit}>
Find
</Button>
</div>
</form>
);
}
function Result({ id, buildid, md5, symbols, download_url }) {
const classes = useStyles();
const [open, setOpen] = useState(false);
const onToggle = useCallback((evt) => {
evt.preventDefault();
setOpen(!open);
}, [open]);
let symbolRows = Object.entries(symbols).map(([k, v]) => (
<TableRow key={k}>
<TableCell><code>{k}</code></TableCell>
<TableCell><code>{v}</code></TableCell>
</TableRow>
));
return (
<div>
<Link href='#' onClick={onToggle}>{id}</Link>
{open && (
<Table size="small" className={classes.table}>
<TableBody>
<TableRow>
<TableCell>Download</TableCell>
<TableCell>
<Link href={download_url} download>Click to download</Link>
</TableCell>
</TableRow>
<TableRow>
<TableCell>BuildID</TableCell>
<TableCell>{buildid}</TableCell>
</TableRow>
<TableRow>
<TableCell>MD5</TableCell>
<TableCell>{md5}</TableCell>
</TableRow>
{symbolRows}
</TableBody>
</Table>
)}
</div>
);
}
function App() {
const [loading, setLoading] = useState(false);
const [results, setResults] = useState(null);
const onSearch = (data) => {
setLoading(true);
(async () => {
try {
setResults(await api('/find', data));
} finally {
setLoading(false);
}
})();
};
return (
<div className="App">
<p>Powered by the <Link href="https://github.com/niklasb/libc-database/tree/master/searchengine">libc-database search API</Link></p>
<Grid container spacing={2}>
<Grid item sm={12} md={6}>
<h3>Search</h3>
<SearchForm onSearch={onSearch} />
</Grid>
<Grid item sm={12} md={6}>
<h3>Results</h3>
{loading && <CircularProgress />}
{results !== null && results.map(x => <Result {...x} />)}
</Grid>
</Grid>
</div>
);
}
export default App;
cd libc-database/searchengine/frontend/
sudo npm install
sudo npm run build
npm install -g cnpm --registry=https://registry.npm.taobao.org
cd libc-database/searchengine/frontend/
sudo cnpm install
cnpm run build
0x04 本地获取所有libc
cd libc-database/
./get ubuntu debian centos kali parrotsec
./get arch
./get rpm
0x05 本地启动查询服务
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
- ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
- ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
server {
- listen 443;
+ listen 80;
server_name libc.rocks;
- ssl on;
- ssl_certificate /etc/letsencrypt/live/libc.rip/fullchain.pem;
- ssl_certificate_key /etc/letsencrypt/live/libc.rip/privkey.pem;
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /download/ {
- alias /home/niklas/libc-database/db/;
+ alias /home/error404/libc-database/db/;
}
location / {
- alias /home/niklas/libc-database/searchengine/frontend/build/;
+ alias /home/error404/libc-database/searchengine/frontend/build/;
}
}
}
import os
ES_INDEX_NAME = 'libcsearch'
# ES_HOST = 'localhost'
ES_HOST = 'es01'
# DB_DIR = os.path.dirname(os.path.abspath(__file__)) + '/../db'
DB_DIR = '/db'
DEFAULT_SYMBOLS = [
'__libc_start_main_ret',
'system',
'dup2',
'str_bin_sh',
'read',
'write',
'puts',
'printf',
]
- DOWNLOAD_URL = 'https://libc.rip/download/{}.so'
+ DOWNLOAD_URL = 'https://127.0.0.1/download/{}.so'
openapi: 3.0.3
info:
version: 1.0.0
title: libc-database search engine
description: ''
paths:
/find:
post:
operationId: app.find
tags:
- libcsearch
description: |-
Look up libc by various attributes
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Libc'
required: true
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Libc'
description: ''
/libc/{id}:
post:
operationId: app.dump
tags:
- libcsearch
parameters:
- in: path
name: id
schema:
type: string
required: true
description: |-
Dump libc symbols
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DumpRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Libc'
description: ''
components:
schemas:
Libc:
type: object
properties:
md5:
type: string
sha1:
type: string
sha256:
type: string
buildid:
type: string
id:
type: string
symbols:
type: object
additionalProperties:
type: string
pattern: '^(0x)?[a-fA-F0-9]+$'
download_url:
type: string
format: url
readOnly: true
DumpRequest:
type: object
properties:
symbols:
type: array
items:
type: string
servers:
- - url: https://libc.rip/api
+ - url: http://127.0.0.1/api
0x06 查询服务的更新
cd libc-database/
./get ubuntu debian centos kali parrotsec
./get arch
./get rpm
python3.6 -m index ../db
0x07 参考链接
看雪ID:勇士小蓝
https://bbs.pediy.com/user-home-816358.htm
往期推荐
球分享
球点赞
球在看